標籤:
本文已授權公眾號:鴻洋(hongyangAndroid)在公眾號平台原創首發。
這篇文章開始, 我們來瞭解一下android 7的一些新特性, 話說今年android 7預覽版本來的比以往都稍早一些, 這樣對於我們開發人員來說算是一個好訊息, 我們可以有充足的時間來看新版android的一些特性, 讓我們的應用更快的支援到android 7. 前段時間android 7發送了最終預覽版本, 這也表示現在的sdk已經是最終的sdk了, 所以我們從現在開始, 完全可以讓應用支援到android 7了.
今天的這篇文章我們來介紹一下在android 7上最為直觀的一個特性-多視窗支援, 當然也可以叫他分屏模式. 有了這個特性媽媽再也不用擔心我應用間切換的煩惱了, 那什麼多視窗模式呢? 其實在很多國產機器上早就已經支援多視窗了, 只不過這次android 7標準化了多視窗模式, 這對我們開發人員來說, 可以算是天大的好訊息. 廢話那麼說, 我們還沒看到多視窗模式什麼樣呢? 下面一張圖來體驗一下.
讓我們的應用支援多視窗模式
如何讓我們的應用支援多視窗模式呢? 其實android 7是預設開啟多視窗模式的, 不過如果你用低於android 7的sdk構建的應用, 會在多視窗模式下發出一個警告. 那如何讓我們的應用禁用多視窗模式呢? 畢竟好多人還是不喜歡讓自己的應用和別人分享螢幕的(比如QQ), 這個也很簡單, 只需要在資訊清單檔的application或者activity中添加android:resizeableActivity="false"就ok了(目測, 接下來大多數國產APP中都會有這條屬性).
多視窗模式的一些配置
禁用歸禁用, 但是對於我們開發人員來說, 還是要繼續瞭解一下多視窗模式的, 那接下來我們來看一下, 在多視窗模式中又會多哪些屬性. 在資訊清單檔中我們配置activity的地方, 又多了一個layout節點, 這個節點的屬性不多, 主要是用來配置多視窗模式下的一些屬性的. 下面我們首先來看看如何配置, 然後來說說都是什麼作用:
<activity android:name=".MyActivity"> <layout android:defaultHeight="500dp" android:defaultWidth="500dp" android:gravity="bottom|end" android:minimalHeight="200dp" android:minimalWidth="200dp" /></activity>
很簡單,就是多了一個layout節點, 我們來看看他的屬性.
android:defaultHeight 這條是配置多視窗模式下預設的高度.
android:defaultWidth 這條是配置多視窗模式下預設的寬度.
android:gravity 這條是配置多視窗模式下activity的初始位置. (注意:這條語句在我測試過程中發現貌似沒起到作用)
android:minimalHeight 這條是配置多視窗模式下最小的高度. (注意:這條語句在我測試過程中發現配置後直接編譯不了)
android:minimalWidth 這條是配置多視窗模式下最小的寬度. (注意:這條語句在我測試過程中發現配置後直接編譯不了)
其實, 就算我們的應用要支援多視窗模式, 上面的layout節點我們也是完全可以忽略的(而且我感覺大部分情況下是要忽略的)
還是看看生命週期
其實, 多視窗本身還是很簡單的, 我們最關心的還是activity在多視窗模式下的生命週期, 下面我們就用一段demo來看一下在多視窗模式下activity的生命週期.
@Overrideprotected void onCreate(Bundle savedInstanceState) { prntLog("onCreate");}@Overrideprotected void onStart() { prntLog("onStart"); super.onStart();}@Overrideprotected void onResume() { prntLog("onResume"); super.onResume();}@Overrideprotected void onPause() { prntLog("onPause"); super.onPause();}@Overrideprotected void onStop() { prntLog("onStop"); super.onStop();}@Overrideprotected void onDestroy() { prntLog("onDestory"); super.onDestroy();}@Overrideprotected void onSaveInstanceState(Bundle outState) { prntLog("onSaveInstanceState"); super.onSaveInstanceState(outState);}@Overrideprotected void onRestoreInstanceState(Bundle savedInstanceState) { prntLog("onRestoreInstanceState"); super.onRestoreInstanceState(savedInstanceState);}@Overridepublic void onMultiWindowModeChanged(boolean isInMultiWindowMode) { prntLog("onMultiWindowModeChanged:" + isInMultiWindowMode); prntLog("isInMultiWindowMode:" + isInMultiWindowMode()); super.onMultiWindowModeChanged(isInMultiWindowMode);}private void prntLog(String log) { Log.d("MainActivity", log);}
在開始之前, 我們發現有一個回調onMultiWindowModeChanged是我們不太熟悉的, 這個回調是為了多視窗模式新增的一個, 在進入或者退出多視窗模式, 這個回調會執行, 而且, 我們還可以用過isInMultiWindowMode()方法來判斷當前activity是否在多視窗模式下. 接下來, 我們來示範一下生命週期吧.
首先是進入多視窗模式(進入多視窗模式的方法是長按手機上的overview鍵)
D/MainActivity: onMultiWindowModeChanged:trueD/MainActivity: isInMultiWindowMode:trueD/MainActivity: onPauseD/MainActivity: onSaveInstanceStateD/MainActivity: onStopD/MainActivity: onDestoryD/MainActivity: onCreateD/MainActivity: onStartD/MainActivity: onRestoreInstanceStateD/MainActivity: onResumeD/MainActivity: onPause
從log中可以發現, 在進入多視窗模式時, 首先回調的是onMultiWindowModeChanged方法, 然後很令人沮喪的是我們的activity銷毀了,並且調用了onSaveInstanceState方法, 然後activit啟動, 其實就是我們activity重啟了.
那退出多視窗模式呢?
D/MainActivity: onSaveInstanceStateD/MainActivity: onStopD/MainActivity: onDestoryD/MainActivity: onCreateD/MainActivity: onStartD/MainActivity: onRestoreInstanceStateD/MainActivity: onResumeD/MainActivity: onPauseD/MainActivity: onMultiWindowModeChanged:falseD/MainActivity: isInMultiWindowMode:falseD/MainActivity: onResume
首先是一個配置變化銷毀的過程, 然後是一個恢複的過程, 並且回調了onMultiWindowModeChanged方法, 此時的isInMultiWindowMode是false.
繼續看生命週期, 如果我們的焦點從一個activity中切換到了和它同處於多視窗模式下的另外一個activity呢?
D/MainActivity: onPauseD/SecondActivity: onResume
此時當前activity會暫停, 新擷取角度的activity回調onResume, 在這裡官網還有一個notice, 比如我們之前是在onPause中暫停視頻播放, 在這種情況下, 失去焦點後就暫停了, 顯然這不是很好的使用者體驗, 我們需要把視頻的暫停和繼續放到onStop和onStart中.
啟動activity
現在我們在來學習下如何在多視窗模式下啟動activity. 這樣分兩種情況了, 一種在是當前棧中啟動, 另一種是在新的棧中啟動.
對於第一種情況, 很簡單, 就是在當前視窗中啟動新的activity, 而第二種情況, 我們可以指定在同層級視窗下啟動, 只需要給intent設定一個FLAG_ACTIVITY_LAUNCH_ADJACENTflag就ok.
例如下面的代碼:
Intent intent = new Intent(this, SecondActivity.class);intent.setFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT|Intent.FLAG_ACTIVITY_NEW_TASK);startActivity(intent);
上面的代碼我們會在另外一個視窗啟動新的activity
另外, 我們還可以制定新啟動的activity的大小.
Rect bounds = new Rect(500, 300, 100, 0);ActivityOptions options = ActivityOptions.makeBasic();options.setLaunchBounds(bounds);Intent intent = new Intent(this, SecondActivity.class);intent.setFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT|Intent.FLAG_ACTIVITY_NEW_TASK);ActivityCompat.startActivity(this, intent, options.toBundle());
跨activity拖拽
從android 4.0開始, android就已經支援activity內的內容拖拽了, 現在在多視窗模式下, android增強了拖拽功能, 另它在多視窗模式下可以在activity間實現內容的拖拽, 不過在activity間也僅限於內容的拖拽, 對view的跨activity拖拽還是不可以的. 現在我們在MainActivity和SecondActivity之前來類比一下跨activity拖拽內容.
// MainActivityButton view = (Button) findViewById(R.id.button);view.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View view) { ClipData data = ClipData.newPlainText(view.getClass().getName(), ((Button) view).getText()); View.DragShadowBuilder builder = new View.DragShadowBuilder(view); view.startDragAndDrop(data, builder, view, View.DRAG_FLAG_GLOBAL); return true; }});
這裡我們監聽button的長按事件, 在長按事件中, 首先我們用button的文本構建一個ClipData對象. 然後調用view.startDragAndDrop方法來啟動拖拽. 這裡要注意一下最後一個參數View.DRAG_FLAG_GLOBAL, 這個flag表示我們可以跨activity進行拖拽.
接著我們來看看SecondActivity如何處理拖拽事件.
final TextView content = (TextView) findViewById(R.id.content);findViewById(R.id.container).setOnDragListener(new View.OnDragListener() {@Overridepublic boolean onDrag(View view, DragEvent dragEvent) { switch (dragEvent.getAction()) { case DragEvent.ACTION_DRAG_STARTED: prntLog("drag started"); break; case DragEvent.ACTION_DRAG_ENTERED: prntLog("drag entered"); break; case DragEvent.ACTION_DROP: ClipData.Item item = dragEvent.getClipData().getItemAt(0); content.setText(item.getText()); break; case DragEvent.ACTION_DRAG_ENDED: prntLog("drag entered"); break; } return true;}});
這裡首先我們拿到根布局(這裡就先不要糾結根布局到底是誰了, 這裡的根布局指的是content_view的根布局), 然後給它設定OnDragListener的監聽, 在ACTION_DROP時候我們通過dragEvent.getClipData().getItemAt(0)拿到拖拽的item, 然後通過getText()方法擷取到內容, 並且設定到TextView上顯示.
來看看效果:
ok, 到現在為止android 7的多視窗模式我們就介紹完了, 這些內容大家有點印象就可以, 畢竟在我們日常的工作中基本一個android:resizeableActivity="false"就可以了.
是時候來瞭解android7了:多視窗支援