美文网首页androidAndroid技术知识Android知识
是时候来了解android7了:多窗口支持

是时候来了解android7了:多窗口支持

作者: 丸_子 | 来源:发表于2016-10-21 15:01 被阅读267次

    这篇文章开始, 我们来了解一下Android 7的一些新特性, 话说今年android 7预览版本来的比以往都稍早一些, 这样对于我们开发者来说算是一个好消息, 我们可以有充足的时间来看新版android的一些特性, 让我们的应用更快的支持到android 7. 前段时间android 7发送了最终预览版本, 这也表示现在的sdk已经是最终的sdk了, 所以我们从现在开始, 完全可以让应用支持到android 7了.

    今天的这篇文章我们来介绍一下在android 7上最为直观的一个特性-多窗口支持, 当然也可以叫他分屏模式. 有了这个特性妈妈再也不用担心我应用间切换的烦恼了, 那什么多窗口模式呢? 其实在很多国产机器上早就已经支持多窗口了, 只不过这次android 7标准化了多窗口模式, 这对我们开发者来说, 可以算是天大的好消息. 废话那么说, 我们还没看到多窗口模式什么样呢? 下面一张图来体验一下.


    1.png

    让我们的应用支持多窗口模式

    如何让我们的应用支持多窗口模式呢? 其实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节点, 我们来看看他的属性.

    1. android:defaultHeight 这条是配置多窗口模式下默认的高度.
    2. android:defaultWidth 这条是配置多窗口模式下默认的宽度.
    3. android:gravity 这条是配置多窗口模式下activity的初始位置. (注意:这条语句在我测试过程中发现貌似没起到作用)
    4. android:minimalHeight 这条是配置多窗口模式下最小的高度. (注意:这条语句在我测试过程中发现配置后直接编译不了)
    5. android:minimalWidth 这条是配置多窗口模式下最小的宽度. (注意:这条语句在我测试过程中发现配置后直接编译不了)

    其实, 就算我们的应用要支持多窗口模式, 上面的layout节点我们也是完全可以忽略的(而且我感觉大部分情况下是要忽略的)

    还是看看生命周期

    其实, 多窗口本身还是很简单的, 我们最关心的还是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);
    }
    

    在开始之前, 我们发现有一个回调onMultiWindowModeChanged是我们不太熟悉的, 这个回调是为了多窗口模式新增的一个, 在进入或者退出多窗口模式, 这个回调会执行, 而且, 我们还可以用过isInMultiWindowMode()方法来判断当前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中可以发现, 在进入多窗口模式时, 首先回调的是onMultiWindowModeChanged方法, 然后很令人沮丧的是我们的activity销毁了,并且调用了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
    

    首先是一个配置变化销毁的过程, 然后是一个恢复的过程, 并且回调了onMultiWindowModeChanged方法, 此时的isInMultiWindowMode
    是false.

    继续看生命周期, 如果我们的焦点从一个activity中切换到了和它同处于多窗口模式下的另外一个activity呢?

    D/MainActivity: onPause
    D/SecondActivity: onResume
    

    此时当前activity会暂停, 新获取角度的activity回调onResume, 在这里官网还有一个notice, 比如我们之前是在onPause中暂停视频播放, 在这种情况下, 失去焦点后就暂停了, 显然这不是很好的用户体验, 我们需要把视频的暂停和继续放到onStop和onStart中.

    启动activity

    现在我们在来学习下如何在多窗口模式下启动activity. 这样分两种情况了, 一种在是当前栈中启动, 另一种是在新的栈中启动. 对于第一种情况, 很简单, 就是在当前窗口中启动新的activity, 而第二种情况, 我们可以指定在同级窗口下启动, 只需要给intent设置一个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


    2.png

    另外, 我们还可以制定新启动的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拖拽内容.

    // 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;
        }
    });
    

    这里我们监听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() {
    @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;
    }
    });
    

    这里首先我们拿到根布局(这里就先不要纠结根布局到底是谁了, 这里的根布局指的是content_view的根布局), 然后给它设置OnDragListener的监听, 在ACTION_DROP时候我们通过dragEvent.getClipData().getItemAt(0)拿到拖拽的item, 然后通过getText()方法获取到内容, 并且设置到TextView上显示.

    来看看效果:


    3.gif

    ok, 到现在为止android 7的多窗口模式我们就介绍完了, 这些内容大家有点印象就可以, 毕竟在我们日常的工作中基本一个android:resizeableActivity="false"就可以了.

    相关文章

      网友评论

      本文标题:是时候来了解android7了:多窗口支持

      本文链接:https://www.haomeiwen.com/subject/ivqcuttx.html