Android N 分屏(多窗口支持)+ConstraintLayout简单使用

Android N 分屏(多窗口支持)+ConstraintLayout简单使用

  1. 特点
    1. Android N 允许多个应用同时共享屏幕
    2. 用户可以在两个 Activity 共享屏幕的同时在这两个 Activity 之间拖放数据 (在此之前,用户只能在一个 Activity 内部拖放数据)。
  2. 多窗口生命周期
    1. 多窗口模式不会更改 Activity 生命周期。
    2. 在多窗口模式中,在指定时间只有最近与用户交互过的 Activity 为活动状态。 该 Activity 将被视为顶级 Activity(onResume)。 所有其他 Activity 虽然可见,但均处于暂停状态(onPause)。 但是,这些已暂停但可见的 Activity 在系统中享有比不可见 Activity 更高的优先级。 如果用户与其中一个暂停的 Activity 交互,该 Activity 将恢复,而之前的顶级 Activity 将暂停。
  3. 配置应用AndroidManifest.xml
    1. android:resizeableActivity="true"设置支持分屏,false为不支持,如果您的应用面向 Android N,但未对该属性指定值,则该属性的值默认设为 true。
    2. 布局属性:
      • android:defaultWidth
        以自由形状模式启动时 Activity 的默认宽度。
      • android:defaultHeight
        以自由形状模式启动时 Activity 的默认高度。
      • android:gravity
        以自由形状模式启动时 Activity 的初始位置。请参阅 Gravity 参考资料,了解合适的值设置。
      • android:minimalHeight、android:minimalWidth
        分屏和自由形状模式中 Activity 的最小高度和最小宽度。 如果用户在分屏模式中移动分界线,使 Activity 尺寸低于指定的最小值,系统会将 Activity 裁剪为用户请求的尺寸。
    3. 示例配置代码:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.tengda.android_70two_pager">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:resizeableActivity="true"
           >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".Main2Activity"
            android:launchMode="singleInstance"
            android:taskAffinity="">
            <layout
                android:defaultHeight="500dp"
                android:defaultWidth="500dp"
                android:gravity="top|end"
                android:minWidth="500dp"
                android:minHeight="500dp" />
        </activity>
        <activity android:name=".Main3Activity"
            android:resizeableActivity="true"
            android:supportsPictureInPicture="true">
        </activity>
    </application>

</manifest>
  1. MainActivity 代码:
public class MainActivity extends AppCompatActivity {
 private ImageView imageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.i("test", "One:" + "onCreate");
        setImageViewEvent();
    }

    //监听是不是进入了分屏模式
    @Override
    public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
        super.onMultiWindowModeChanged(isInMultiWindowMode);
        Log.i("test", "One:" + "onMultiWindowModeChanged" + isInMultiWindowMode);
    }

    //当Activity进入画中画模式时,系统会回调这个方法
    @Override
    public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
        super.onPictureInPictureModeChanged(isInPictureInPictureMode);
        Log.i("test", "One:" + "onPictureInPictureModeChanged" + isInPictureInPictureMode);
    }

    //如果用户重新调整窗口的大小,系统在必要的时候也可能触发
    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        Log.i("test", "One:" + "onConfigurationChanged"+newConfig.toString());
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.i("test", "One:" + "onStart");
    }

    @Override
    protected void onRestart() {
        super.onRestart();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            Log.i("test", "One:" + "onRestart" + ",判断是否处于分屏模式:" + this.isInMultiWindowMode() + ",判断是否处于画中画模式" + this.isInPictureInPictureMode());
        } else {
            Log.i("test", "One:" + "onRestart");
        }
    }

    //分屏模式从失去焦点——》得到焦点,执行
    @Override
    protected void onResume() {
        super.onResume();
        Log.i("test", "One:" + "onResume");

    }

    //分屏模式下失去焦点,执行
    @Override
    protected void onPause() {
        super.onPause();
        Log.i("test", "One:" + "onPause");

    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.i("test", "One:" + "onStop");

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.i("test", "One:" + "onDestroy");
    }


    //打开第二个activity
    public void openSencondActivity(View view) {
        //1、当前MainActivity已经进入到分屏模式。
        //2、新打开的Main2Activity支持分屏浏览(即android:resizeableActivity=true)。
        //满足1和2,打开的Main2Activity将与MainActivity实现分屏
        startActivity(new Intent(this, Main2Activity.class).addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT | Intent.FLAG_ACTIVITY_NEW_TASK));
        /**
            设置FLAG_ACTIVITY_NEW_TASK的原因:
        1、在同一个Activity返回栈中,打开一个新的Activity时,这个Activity将会继承上一个Activity所有和分屏模式有关的属性。
        如果你想要在一个独立的窗口以分屏模式打开一个新的Activity,那么必须新建一个Activity返回栈。
         */
        /**
            此外,如果你的设备支持自由模式(官方名字叫freeform,暂且就这么翻译它,其实我认为这算也是一种尺寸更自由的分屏模式,
        如果设备厂商支持用户可以自由改变Activity的尺寸,那么就相当于支持自由模式,这将比普通的分屏模式更加自由),
        打开一个Activity时,还可通过ActivityOptions.setLaunchBounds()来指定新的Activity的尺寸和在屏幕中的位置。
        同样,这个方法也需要你的Activity已经处于分屏模式时,调用它才会生效。
        */
    }

    /**
     * 支持拖放
     * 用户可以在两个 Activity 共享屏幕的同时在这两个 Activity 之间拖放数据 (在此之前,用户只能在一个 Activity 内部拖放数据)。
     */
    public void setImageViewEvent() {
        imageView = (ImageView) findViewById(R.id.imageView);
        /** 拖拽的发送方Activity和ImageView */
        imageView.setTag("I'm a ImageView from MainActivity");
        imageView.setOnTouchListener(new View.OnTouchListener() {

            @RequiresApi(api = Build.VERSION_CODES.N)
            public boolean onTouch(View view, MotionEvent motionEvent) {
                if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
                    /** 构造一个ClipData,将需要传递的数据放在里面 */
                    ClipData.Item item = new ClipData.Item((CharSequence) view.getTag());
                    String[] mimeTypes = {ClipDescription.MIMETYPE_TEXT_PLAIN};
                    ClipData dragData = new ClipData(view.getTag().toString(), mimeTypes, item);
                    MyDragShadowBuilder shadow = new MyDragShadowBuilder(imageView);//替换当前正在进行的拖动操作的拖动阴影。只能由发起拖动操作的应用调用
                    /** startDragAndDrop是Android N SDK中的新方法,替代了以前的startDrag(),
                     *flag需要设置为DRAG_FLAG_GLOBAL ( 需要传递View.DRAG_FLAG_GLOBAL来实现跨Activity拖拽。)
                     * 如果需要将URI权限传递给接收方Activity,还可以根据需要设置View.DRAG_FLAG_GLOBAL_URI_READ或者View.DRAG_FLAG_GLOBAL_URI_WRITE。
                     * */
                    view.startDragAndDrop(dragData, shadow, null, View.DRAG_FLAG_GLOBAL);
                    /**
                     * Activity.requestDropPermissions()
                     请求使用 DragEvent 中包含的 ClipData 传递的内容 URI 的权限。
                     */
                    return true;
                } else {
                    return false;
                }
            }
        });
    }

    class MyDragShadowBuilder extends View.DragShadowBuilder {

        // The drag shadow image, defined as a drawable thing
        private Drawable shadow;

        // Defines the constructor for myDragShadowBuilder
        public MyDragShadowBuilder(View v) {

            // Stores the View parameter passed to myDragShadowBuilder.
            super(v);
            // Creates a draggable image that will fill the Canvas provided by the system.
            shadow = new ColorDrawable(Color.LTGRAY);
        }

        // Defines a callback that sends the drag shadow dimensions and touch point back to the
        // system.
        @Override
        public void onProvideShadowMetrics(Point size, Point touch) {
            // Defines local variables
            int width, height;

            // Sets the width of the shadow to half the width of the original View
            width = getView().getWidth() / 2;

            // Sets the height of the shadow to half the height of the original View
            height = getView().getHeight() / 2;

            // The drag shadow is a ColorDrawable. This sets its dimensions to be the same as the
            // Canvas that the system will provide. As a result, the drag shadow will fill the
            // Canvas.
            shadow.setBounds(0, 0, width, height);

            // Sets the size parameter's width and height values. These get back to the system
            // through the size parameter.
            size.set(width, height);

            // Sets the touch point's position to be in the middle of the drag shadow
            touch.set(width / 2, height / 2);
        }

        // Defines a callback that draws the drag shadow in a Canvas that the system constructs
        // from the dimensions passed in onProvideShadowMetrics().
        @Override
        public void onDrawShadow(Canvas canvas) {

            // Draws the ColorDrawable in the Canvas passed in from the system.
            shadow.draw(canvas);
        }
    }
}

activity_main.xml布局文件:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.tengda.android_70two_pager.MainActivity"
    tools:ignore="MissingConstraints">

    <Button
        android:text="在另一块分屏中打开secondActivity"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/button"
        android:onClick="openSencondActivity"
        app:layout_constraintTop_toTopOf="@+id/activity_main"
        app:layout_constraintLeft_toLeftOf="@+id/activity_main"
        tools:ignore="HardcodedText"
        app:layout_constraintRight_toRightOf="@+id/activity_main"
        app:layout_constraintBottom_toBottomOf="@+id/activity_main"
        />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:srcCompat="@mipmap/ic_launcher"
        android:id="@+id/imageView"
        app:layout_constraintBottom_toTopOf="@+id/button"
        android:layout_marginBottom="24dp"
        android:layout_marginTop="16dp"
        app:layout_constraintTop_toTopOf="@+id/activity_main"
        android:layout_marginEnd="16dp"
        app:layout_constraintRight_toRightOf="@+id/activity_main"
        android:layout_marginStart="16dp"
        app:layout_constraintLeft_toLeftOf="@+id/activity_main"
        tools:ignore="ContentDescription" />

    <Button
        android:text="打开画中画Activity"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/button2"
        android:layout_marginTop="8dp"
        app:layout_constraintTop_toBottomOf="@+id/button"
        app:layout_constraintBottom_toBottomOf="@+id/activity_main"
        android:layout_marginBottom="16dp"
        android:layout_marginEnd="16dp"
        app:layout_constraintRight_toRightOf="@+id/activity_main"
        android:layout_marginStart="16dp"
        app:layout_constraintLeft_toLeftOf="@+id/activity_main"
        tools:ignore="HardcodedText"
        android:onClick="openMain3Activity" />
</android.support.constraint.ConstraintLayout>
  1. Main2Activity
public class Main2Activity extends AppCompatActivity {

    TextView dropedText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        getFromMainActivityMsg();
    }
//接收从MainActivity拖过来的图片中的消息
    public void getFromMainActivityMsg() {
        dropedText = (TextView) findViewById(R.id.textView);
        dropedText.setOnDragListener(new View.OnDragListener() {
            @Override
            public boolean onDrag(View view, DragEvent dragEvent) {
                switch (dragEvent.getAction()) {
                    case DragEvent.ACTION_DRAG_STARTED:
                        Log.d("test", "Action is DragEvent.ACTION_DRAG_STARTED");
                        break;

                    case DragEvent.ACTION_DRAG_ENTERED:
                        Log.d("test", "Action is DragEvent.ACTION_DRAG_ENTERED");
                        break;

                    case DragEvent.ACTION_DRAG_EXITED:
                        Log.d("test", "Action is DragEvent.ACTION_DRAG_EXITED");
                        break;

                    case DragEvent.ACTION_DRAG_LOCATION:
                        break;

                    case DragEvent.ACTION_DRAG_ENDED:
                        Log.d("test", "Action is DragEvent.ACTION_DRAG_ENDED");
                        break;

                    case DragEvent.ACTION_DROP:
                        Log.d("test", "ACTION_DROP event");
                        /** 3.在这里显示接收到的结果 */
                        dropedText.setText(dragEvent.getClipData().getItemAt(0).getText());
                        break;

                    default:
                        break;
                }

                return true;
            }
        });
    }
}

layout/activity_main2.xml布局文件:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main2"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.tengda.android_70two_pager.Main2Activity"
    tools:ignore="MissingConstraints">

    <TextView
        android:text="第二个Activity"
        android:layout_width="190dp"
        android:layout_height="115dp"
        android:id="@+id/textView"
        android:gravity="center_horizontal|center_vertical"
        app:layout_constraintTop_toTopOf="@+id/activity_main2"
        app:layout_constraintLeft_toLeftOf="@+id/activity_main2"
        app:layout_constraintRight_toRightOf="@+id/activity_main2"
        app:layout_constraintBottom_toBottomOf="@+id/activity_main2"
        tools:ignore="HardcodedText" />
</android.support.constraint.ConstraintLayout>

注:
1. 使用Android N 中的约束布局 android.support.constraint.ConstraintLayout需在build.gradle——》dependencies——》添加 compile ‘com.android.support.constraint:constraint-layout:1.0.0-alpha2’(使用起来还是比较简单的。。。)
2. 画中画现只支持Android_tv
附:
1. 官方文档之多窗口支持连接:https://developer.android.com/guide/topics/ui/multi-window.html?hl=zh-cn#overview
2. 官方文档之画中画:https://developer.android.com/training/tv/playback/picture-in-picture.html?hl=zh-cn#pip_button

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值