Android NDK开发详解大屏设备之activity 嵌入四


响应分屏状态变化
应用中的不同 activity 可以具有执行相同功能的界面元素;例如,一个用于打开包含账号设置的窗口的控件。

在这里插入图片描述

图 23. 不同的 activity 具有功能上完全相同的界面元素。
如果分屏中有两个 activity 具有共同的界面元素,那么这两个 activity 中都显示该元素就是多余的,而且可能会令人感到困惑。

在这里插入图片描述

图 24. activity 分屏中的重复界面元素。
如需了解 activity 何时出现在分屏中,请检查 SplitController.splitInfoList 流或通过 SplitControllerCallbackAdapter 注册一个监听器来监听分屏状态的变化。然后,相应地调整界面:

Kotlin

val layout = layoutInflater.inflate(R.layout.activity_main, null)
val view = layout.findViewById<View>(R.id.infoButton)
lifecycleScope.launch {
    lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
        splitController.splitInfoList(this@SplitDeviceActivity) // The activity instance.
            .collect { list ->
                view.visibility = if (list.isEmpty()) View.VISIBLE else View.GONE
            }
    }
}

Java

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    . . .
    new SplitControllerCallbackAdapter(SplitController.getInstance(this))
        .addSplitListener(
            this,
            Runnable::run,
            splitInfoList -> {
                View layout = getLayoutInflater().inflate(R.layout.activity_main, null);
                layout.findViewById(R.id.infoButton).setVisibility(
                    splitInfoList.isEmpty() ? View.VISIBLE : View.GONE);
            });
}

协程可在任何生命周期状态下启动,但通常在 STARTED 状态下启动,以节省资源(如需了解详情,请参阅将 Kotlin 协程与生命周期感知型组件一起使用)。

可以在任何生命周期状态下进行回调,包括当 activity 停止时。通常应在 onStart() 中注册监听器,在 onStop() 中取消注册监听器。

注意:请仅对 Java 应用使用回调。针对 Kotlin 应用使用 splitInfoList 流 API。
如需在应用中访问 SplitControllerCallbackAdapter,请在应用的模块级 build.gradle 文件中向最新版本的 androidx.window:window-java 库添加一个依赖项。

全窗口模态

某些 activity 会阻止用户与应用互动,直到执行了指定的操作;例如,登录屏幕 activity、政策确认屏幕或错误消息。应防止模态 activity 出现在分屏中。

您可以使用展开配置来强制 activity 始终填满任务窗口:

<ActivityRule
    window:alwaysExpand="true">
    <ActivityFilter
        window:activityName=".FullWidthActivity"/>
</ActivityRule>

结束 activity

用户可以通过从显示屏的边缘滑动,在分屏的任意一侧结束 activity:

在这里插入图片描述

图 25. 结束 activity B 的滑动手势。
在这里插入图片描述

图 26. 结束 activity A 的滑动手势。
如果设备设置为使用返回按钮而不是手势导航,则系统会将输入发送到聚焦的 activity,即上次轻触或启动的 activity。

结束容器中所有 activity 对对立容器的影响取决于分屏配置。

配置属性

您可以指定分屏规则属性,以便配置在分屏一侧结束所有 activity 如何影响分屏另一侧的 activity。这些属性包括:

window:finishPrimaryWithSecondary:结束辅助容器中的所有 activity 对主要容器中的 activity 有何影响
window:finishSecondaryWithPrimary:完成主要容器中的所有 activity 对辅助容器中的 activity 有何影响
可能的属性值包括:

always:始终完成关联容器中的 activity
never:绝不完成关联容器中的 activity
adjacent:当两个容器彼此相邻显示时,结束关联容器中的 activity;但当两个容器堆叠时,不结束这些 activity
例如:

<SplitPairRule
    <!-- Do not finish primary container activities when all secondary container activities finish. -->
    window:finishPrimaryWithSecondary="never"
    <!-- Finish secondary container activities when all primary container activities finish. -->
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

默认配置

当分屏中一个容器内的所有 activity 都结束时,其余的容器会占据整个窗口:


<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

分屏包含 activity A 和 activity B。activity A 已完成,留下 activity B 占据整个窗口。
在这里插入图片描述

分屏包含 activity A 和 activity B。activity B 已完成,留下 activity A 占据整个窗口。
在这里插入图片描述

一起结束 activity

当辅助容器中的所有 activity 都结束时,自动结束主要容器中的 activity:


<SplitPairRule
    window:finishPrimaryWithSecondary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

分屏包含 activity A 和 activity B。activity B 已完成,activity A 也随之完成,这使得任务窗口为空。
在这里插入图片描述

分屏包含 activity A 和 activity B。activity A 已完成,在任务窗口中只留下 activity B。
在这里插入图片描述

当主要容器中的所有 activity 都结束时,自动结束辅助容器中的 activity:

<SplitPairRule
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

分屏包含 activity A 和 activity B。activity A 已完成,activity B 也随之完成,这使得任务窗口为空。
在这里插入图片描述

分屏包含 activity A 和 activity B。activity B 已完成,在任务窗口中只留下 activity A。
在这里插入图片描述

当主要容器或辅助容器中的所有 activity 都结束时,一起结束这些 activity:

<SplitPairRule
    window:finishPrimaryWithSecondary="always"
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

分屏包含 activity A 和 activity B。activity A 已完成,activity B 也随之完成,这使得任务窗口为空。
在这里插入图片描述

分屏包含 activity A 和 activity B。activity B 已完成,activity A 也随之完成,这使得任务窗口为空。
在这里插入图片描述

结束容器中的多个 activity

如果分屏容器中堆叠了多个 activity,结束堆栈底层的 activity 并不会自动结束它上面的 activity。

例如,如果辅助容器中有两个 activity,其中 activity C 在 activity B 之上:
在这里插入图片描述

辅助 activity 堆栈(其中 activity C 堆叠在 activity B 之上)堆叠在主要 activity 堆栈(包含 activity A)之上。

并且分屏的配置由 activity A 和 activity B 的配置定义:

<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

那么,结束顶层 activity 时,会保留分屏。
在这里插入图片描述

一个分屏,其中 activity A 在主要容器中,activity B 和 activity C 在辅助容器中,且 activity C 堆叠在 activity B 之上。activity C 已结束,将 activity A 和 activity B 留在 activity 分屏中。

结束辅助容器的底层(根)activity 并不会移除它上面的 activity;因此,仍会保留分屏。
在这里插入图片描述

一个分屏,其中 activity A 在主要容器中,activity B 和 activity C 在辅助容器中,且 activity C 堆叠在 activity B 之上。activity B 已结束,将 activity A 和 activity C 留在 activity 分屏中。

也会执行关于一起结束 activity 的其他任何规则,如将辅助 activity 与主要 activity 一起结束:

<SplitPairRule
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

一个分屏,其中 activity A 在主要容器中,activity B 和 activity C 在辅助容器中,且 activity C 堆叠在 activity B 之上。activity A 已完成,activity B 和 activity C 也随之完成。
在这里插入图片描述

将分屏配置为一起结束主要 activity 和辅助 activity 时:

<SplitPairRule
    window:finishPrimaryWithSecondary="always"
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

一个分屏,其中 activity A 在主要容器中,activity B 和 activity C 在辅助容器中,且 activity C 堆叠在 activity B 之上。activity C 已结束,将 activity A 和 activity B 留在 activity 分屏中。
在这里插入图片描述

一个分屏,其中 activity A 在主要容器中,activity B 和 activity C 在辅助容器中,且 activity C 堆叠在 activity B 之上。activity B 已结束,将 activity A 和 activity C 留在 activity 分屏中。
在这里插入图片描述

一个分屏,其中 activity A 在主要容器中,activity B 和 activity C 在辅助容器中,且 activity C 堆叠在 activity B 之上。activity A 已完成,activity B 和 activity C 也随之完成。
在这里插入图片描述

在运行时更改分屏属性

您不能更改当前活跃和可见的分屏的属性。更改分屏规则会影响其他 activity 启动和新容器,但不会影响现有和活跃分屏。

如需更改活跃分屏的属性,请结束分屏中侧面的一个或多个 activity,然后使用新配置再次启动到侧面。

将 activity 从分屏提取到全窗口

创建显示侧面 activity 全窗口的新配置,然后使用解析为同一实例的 intent 重新启动 activity。

在运行时检查分屏支持

Android 12L(API 级别 32)及更高版本支持 activity 嵌入,但某些搭载更低平台版本的设备也支持 activity 嵌入。如需在运行时检查该功能是否可用,请使用 SplitController.splitSupportStatus 属性或 SplitController.getSplitSupportStatus() 方法:

Kotlin

if (SplitController.getInstance(this).splitSupportStatus ==
     SplitController.SplitSupportStatus.SPLIT_AVAILABLE) {
     // Device supports split activity features.
}

Java

if (SplitController.getInstance(this).getSplitSupportStatus() ==
     SplitController.SplitSupportStatus.SPLIT_AVAILABLE) {
     // Device supports split activity features.
}

如果不支持分屏,系统会在 activity 堆栈顶部启动 activity(遵循非 activity 嵌入模型)。

阻止系统替换

Android 设备的制造商(原始设备制造商)可将 activity 嵌入作为设备系统的函数来实现。系统会为多 activity 应用指定分屏规则,从而替换应用的窗口行为。系统替换会强制多 activity 应用进入系统定义的 activity 嵌入模式。

系统 activity 嵌入可通过多窗格布局(例如列表-详情)增强应用呈现效果,而无需对应用进行任何更改。不过,系统的 activity 嵌入也可能会导致应用布局不正确、出现 bug 或与应用所实现的 activity 嵌入冲突。

您的应用可通过在应用清单文件中设置属性来阻止或允许系统 activity 嵌入,例如:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application>
        <property
            android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE"
            android:value="true|false" />
    </application>
</manifest>

属性名称在 Jetpack WindowManager WindowProperties 对象中定义。如果您的应用实现了 activity 嵌入,或者您想阻止系统将其 activity 嵌入规则应用于您的应用,请将该值设为 false;若想允许系统将系统定义的 activity 嵌入应用于您的应用,请将值设为 true。

注意:OEM 可针对任何 Android 版本或 API 级别实现系统 activity 嵌入。无论应用的目标 API 级别如何,都应始终设置 android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE 属性。

限制条件和注意事项

只有任务的托管应用(标识为任务中根 activity 的所有者)才能在任务中组织和嵌入其他 activity。如果支持嵌入和分屏的 activity 在属于其他应用的任务中运行,则嵌入和分屏将不适用于这些 activity。
只能在单个任务中组织 activity。在新任务中启动 activity 时,始终都会将其放置在所有现有分屏之外的新展开窗口中。
只能将同一进程中的 activity 整理放置在分屏中。SplitInfo 回调仅报告属于同一进程的 activity,因为无法知道其他进程中的 activity。
每对或单个 activity 规则仅适用于在注册该规则后发生的 activity 启动。目前无法更新现有分屏或其视觉属性。
分屏对过滤器配置必须与启动 activity 时使用的 intent 完全匹配。从应用进程中启动新的 activity 时会发生匹配,因此使用隐式 intent 时,可能不知道稍后在系统进程中解析的组件名称。如果在启动时不知道组件名称,可以改用通配符(“/”),系统会根据 intent 操作执行过滤。
目前无法在容器之间移动 activity,也无法在创建分屏后将 activity 移入和移出分屏。只有在启动具有匹配规则的新 activity 时,WindowManager 库才会创建分屏;当分屏容器中的最后一个 activity 结束时,分屏会被销毁。
当配置发生更改时可以重新启动 activity,因此在创建或移除了分屏以及 activity 边界发生更改时,activity 可以完全销毁之前的实例,并创建一个新的实例。因此,对于诸如从生命周期回调启动新 activity 之类的操作,应用开发者应格外小心。
设备必须包含窗口扩展接口,以支持 activity 嵌入。几乎所有搭载 Android 12L(API 级别 32)或更高版本的大屏幕设备均包含接口。不过,一些无法运行多个 activity 的大屏幕设备未包含窗口扩展接口。如果大屏设备不支持多窗口模式,则可能不支持 activity 嵌入。

其他资源

Codelab - 通过 activity 嵌入构建列表-详情布局
学习开发者在线课程 - activity 嵌入
本页面上的内容和代码示例受内容许可部分所述许可的限制。Java 和 OpenJDK 是 Oracle 和/或其关联公司的注册商标。

最后更新时间 (UTC):2023-11-10。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

五一编程

程序之路有我与你同行

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值