這篇文章開始, 我們來了解一下Android 7的一些新特性, 話說今年android 7預(yù)覽版本來的比以往都稍早一些, 這樣對于我們開發(fā)者來說算是一個好消息, 我們可以有充足的時間來看新版android的一些特性, 讓我們的應(yīng)用更快的支持到android 7. 前段時間android 7發(fā)送了最終預(yù)覽版本, 這也表示現(xiàn)在的sdk已經(jīng)是最終的sdk了, 所以我們從現(xiàn)在開始, 完全可以讓應(yīng)用支持到android 7了.
今天的這篇文章我們來介紹一下在android 7上最為直觀的一個特性-多窗口支持, 當(dāng)然也可以叫他分屏模式. 有了這個特性媽媽再也不用擔(dān)心我應(yīng)用間切換的煩惱了, 那什么多窗口模式呢? 其實在很多國產(chǎn)機器上早就已經(jīng)支持多窗口了, 只不過這次android 7標(biāo)準(zhǔn)化了多窗口模式, 這對我們開發(fā)者來說, 可以算是天大的好消息. 廢話那么說, 我們還沒看到多窗口模式什么樣呢? 下面一張圖來體驗一下.
讓我們的應(yīng)用支持多窗口模式
如何讓我們的應(yīng)用支持多窗口模式呢? 其實android 7是默認開啟多窗口模式的, 不過如果你用低于android 7的sdk構(gòu)建的應(yīng)用, 會在多窗口模式下發(fā)出一個警告. 那如何讓我們的應(yīng)用禁用多窗口模式呢? 畢竟好多人還是不喜歡讓自己的應(yīng)用和別人分享屏幕的(比如QQ), 這個也很簡單, 只需要在清單文件的application或者activity中添加 android:resizeableActivity="false"
就ok了(目測, 接下來大多數(shù)國產(chǎn)APP中都會有這條屬性).
多窗口模式的一些配置
禁用歸禁用, 但是對于我們開發(fā)者來說, 還是要繼續(xù)了解一下多窗口模式的, 那接下來我們來看一下, 在多窗口模式中又會多哪些屬性. 在清單文件中我們配置activity的地方, 又多了一個layout節(jié)點, 這個節(jié)點的屬性不多, 主要是用來配置多窗口模式下的一些屬性的. 下面我們首先來看看如何配置, 然后來說說都是什么作用:
<activity android:name=".MyActivity">
<layout
android:defaultHeight="500dp"
android:defaultWidth="500dp"
android:gravity="bottom|end"
android:minimalHeight="200dp"
android:minimalWidth="200dp" />
</activity>
很簡單,就是多了一個layout節(jié)點, 我們來看看他的屬性.
- android:defaultHeight 這條是配置多窗口模式下默認的高度.
- android:defaultWidth 這條是配置多窗口模式下默認的寬度.
- android:gravity 這條是配置多窗口模式下activity的初始位置. (注意:這條語句在我測試過程中發(fā)現(xiàn)貌似沒起到作用)
- android:minimalHeight 這條是配置多窗口模式下最小的高度. (注意:這條語句在我測試過程中發(fā)現(xiàn)配置后直接編譯不了)
- android:minimalWidth 這條是配置多窗口模式下最小的寬度. (注意:這條語句在我測試過程中發(fā)現(xiàn)配置后直接編譯不了)
其實, 就算我們的應(yīng)用要支持多窗口模式, 上面的layout節(jié)點我們也是完全可以忽略的(而且我感覺大部分情況下是要忽略的)
還是看看生命周期
其實, 多窗口本身還是很簡單的, 我們最關(guān)心的還是activity在多窗口模式下的生命周期, 下面我們就用一段demo來看一下在多窗口模式下activity的生命周期.
@Override
protected void onCreate(Bundle savedInstanceState) {
prntLog("onCreate");
}
@Override
protected void onStart() {
prntLog("onStart");
super.onStart();
}
@Override
protected void onResume() {
prntLog("onResume");
super.onResume();
}
@Override
protected void onPause() {
prntLog("onPause");
super.onPause();
}
@Override
protected void onStop() {
prntLog("onStop");
super.onStop();
}
@Override
protected void onDestroy() {
prntLog("onDestory");
super.onDestroy();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
prntLog("onSaveInstanceState");
super.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
prntLog("onRestoreInstanceState");
super.onRestoreInstanceState(savedInstanceState);
}
@Override
public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
prntLog("onMultiWindowModeChanged:" + isInMultiWindowMode);
prntLog("isInMultiWindowMode:" + isInMultiWindowMode());
super.onMultiWindowModeChanged(isInMultiWindowMode);
}
private void prntLog(String log) {
Log.d("MainActivity", log);
}
在開始之前, 我們發(fā)現(xiàn)有一個回調(diào)onMultiWindowModeChanged是我們不太熟悉的, 這個回調(diào)是為了多窗口模式新增的一個, 在進入或者退出多窗口模式, 這個回調(diào)會執(zhí)行, 而且, 我們還可以用過isInMultiWindowMode()方法來判斷當(dāng)前activity是否在多窗口模式下. 接下來, 我們來演示一下生命周期吧.
首先是進入多窗口模式(進入多窗口模式的方法是長按手機上的overview鍵)
D/MainActivity: onMultiWindowModeChanged:true
D/MainActivity: isInMultiWindowMode:true
D/MainActivity: onPause
D/MainActivity: onSaveInstanceState
D/MainActivity: onStop
D/MainActivity: onDestory
D/MainActivity: onCreate
D/MainActivity: onStart
D/MainActivity: onRestoreInstanceState
D/MainActivity: onResume
D/MainActivity: onPause
從log中可以發(fā)現(xiàn), 在進入多窗口模式時, 首先回調(diào)的是onMultiWindowModeChanged方法, 然后很令人沮喪的是我們的activity銷毀了,并且調(diào)用了onSaveInstanceState方法, 然后activit啟動, 其實就是我們activity重啟了.
那退出多窗口模式呢?
D/MainActivity: onSaveInstanceState
D/MainActivity: onStop
D/MainActivity: onDestory
D/MainActivity: onCreate
D/MainActivity: onStart
D/MainActivity: onRestoreInstanceState
D/MainActivity: onResume
D/MainActivity: onPause
D/MainActivity: onMultiWindowModeChanged:false
D/MainActivity: isInMultiWindowMode:false
D/MainActivity: onResume
首先是一個配置變化銷毀的過程, 然后是一個恢復(fù)的過程, 并且回調(diào)了onMultiWindowModeChanged方法, 此時的isInMultiWindowMode
是false.
繼續(xù)看生命周期, 如果我們的焦點從一個activity中切換到了和它同處于多窗口模式下的另外一個activity呢?
D/MainActivity: onPause
D/SecondActivity: onResume
此時當(dāng)前activity會暫停, 新獲取角度的activity回調(diào)onResume, 在這里官網(wǎng)還有一個notice, 比如我們之前是在onPause中暫停視頻播放, 在這種情況下, 失去焦點后就暫停了, 顯然這不是很好的用戶體驗, 我們需要把視頻的暫停和繼續(xù)放到onStop和onStart中.
啟動activity
現(xiàn)在我們在來學(xué)習(xí)下如何在多窗口模式下啟動activity. 這樣分兩種情況了, 一種在是當(dāng)前棧中啟動, 另一種是在新的棧中啟動. 對于第一種情況, 很簡單, 就是在當(dāng)前窗口中啟動新的activity, 而第二種情況, 我們可以指定在同級窗口下啟動, 只需要給intent設(shè)置一個FLAG_ACTIVITY_LAUNCH_ADJACENT flag就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就已經(jīng)支持activity內(nèi)的內(nèi)容拖拽了, 現(xiàn)在在多窗口模式下, android增強了拖拽功能, 另它在多窗口模式下可以在activity間實現(xiàn)內(nèi)容的拖拽, 不過在activity間也僅限于內(nèi)容的拖拽, 對view的跨activity拖拽還是不可以的. 現(xiàn)在我們在MainActivity和SecondActivity之前來模擬一下跨activity拖拽內(nèi)容.
// MainActivity
Button 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;
}
});
這里我們監(jiān)聽button的長按事件, 在長按事件中, 首先我們用button的文本構(gòu)建一個ClipData對象. 然后調(diào)用view.startDragAndDrop方法來啟動拖拽. 這里要注意一下最后一個參數(shù)View.DRAG_FLAG_GLOBAL, 這個flag表示我們可以跨activity進行拖拽.
接著我們來看看SecondActivity如何處理拖拽事件.
final TextView content = (TextView) findViewById(R.id.content);
findViewById(R.id.container).setOnDragListener(new View.OnDragListener() {
@Override
public 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;
}
});
這里首先我們拿到根布局(這里就先不要糾結(jié)根布局到底是誰了, 這里的根布局指的是content_view的根布局), 然后給它設(shè)置OnDragListener的監(jiān)聽, 在ACTION_DROP時候我們通過dragEvent.getClipData().getItemAt(0)拿到拖拽的item, 然后通過getText()方法獲取到內(nèi)容, 并且設(shè)置到TextView上顯示.
來看看效果:
ok, 到現(xiàn)在為止android 7的多窗口模式我們就介紹完了, 這些內(nèi)容大家有點印象就可以, 畢竟在我們?nèi)粘5墓ぷ髦谢疽粋€android:resizeableActivity="false"就可以了.