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

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

固定的纵向屏幕方向

android:screenOrientation 清单设置让应用能够将 activity 限制为纵向或横向。为提升平板电脑和可折叠设备等大屏设备上的用户体验,设备制造商 (OEM) 会忽略屏幕方向要求,并采用信箱模式,在横屏下纵向显示应用或在竖屏下横向显示应用。
在这里插入图片描述

图 12. 采用信箱模式的 activity:在横屏设备上固定为竖屏显示(左侧),在竖屏设备上固定为横屏显示(右侧)。
同理,启用 activity 嵌入后,OEM 能够自定义设备,通过信箱模式在屏幕方向为横屏的大屏设备(宽度 ≥ 600dp)上呈现固定竖屏的 activity。当固定纵向的 activity 启动第二个 activity 时,设备会在双窗格显示屏中并排显示这两个 activity。
在这里插入图片描述

图 13. 固定纵向的 activity A 在侧面启动 activity B。
将 android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED 属性一律添加到您的应用清单文件中,告知设备您的应用支持 activity 嵌入(请参阅下文的分屏配置)。这样一来,OEM 自定义的设备就可以确定是否采用信箱模式呈现固定纵向的 activity。

分屏配置

分屏规则用于配置 activity 分屏。您可以在 XML 配置文件中或通过进行 Jetpack WindowManager API 调用来定义分屏规则。

无论是哪种情况,应用都必须访问 WindowManager 库,并且必须通知系统应用已实现 activity 嵌入。

请执行以下操作:

将最新的 WindowManager 库依赖项添加到应用的模块级 build.gradle 文件中,例如:

implementation 'androidx.window:window:1.1.0-beta02'

注意:如需了解发布版本,请参阅 WindowManager 版本说明。
WindowManager 库提供了 activity 嵌入所需的所有组件。

告知系统您的应用已实现 activity 嵌入。

将 android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED 属性添加到应用清单文件中的 元素,然后将该值设置为 true,例如:

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

在 WindowManager 版本 1.1.0-alpha06 及更高版本中,除非将该属性添加到清单中并设置为 true,否则系统会停用 activity 嵌入分屏。

此外,设备制造商会使用该设置来为支持 activity 嵌入的应用启用自定义功能。例如,设备可以在横向显示屏上将仅限纵向模式的 activity 设为信箱模式,以便在第二个 activity 启动时,让该 activity 转换为双窗格布局(请参阅固定的纵向屏幕方向)。

XML 配置

如需创建基于 XML 的 activity 嵌入实现,请完成以下步骤:

创建一个执行以下操作的 XML 资源文件:

定义共享分屏的 activity
配置分屏选项
在没有可用内容时,为分屏的辅助容器创建占位符
指定绝不应属于分屏的 activity
例如:

<!-- main_split_config.xml -->

<resources
    xmlns:window="http://schemas.android.com/apk/res-auto">

    <!-- Define a split for the named activities. -->
    <SplitPairRule
        window:splitRatio="0.33"
        window:splitLayoutDirection="locale"
        window:splitMinWidthDp="840"
        window:splitMaxAspectRatioInPortrait="alwaysAllow"
        window:finishPrimaryWithSecondary="never"
        window:finishSecondaryWithPrimary="always"
        window:clearTop="false">
        <SplitPairFilter
            window:primaryActivityName=".ListActivity"
            window:secondaryActivityName=".DetailActivity"/>
    </SplitPairRule>

    <!-- Specify a placeholder for the secondary container when content is
         not available. -->
    <SplitPlaceholderRule
        window:placeholderActivityName=".PlaceholderActivity"
        window:splitRatio="0.33"
        window:splitLayoutDirection="locale"
        window:splitMinWidthDp="840"
        window:splitMaxAspectRatioInPortrait="alwaysAllow"
        window:stickyPlaceholder="false">
        <ActivityFilter
            window:activityName=".ListActivity"/>
    </SplitPlaceholderRule>

    <!-- Define activities that should never be part of a split. Note: Takes
         precedence over other split rules for the activity named in the
         rule. -->
    <ActivityRule
        window:alwaysExpand="true">
        <ActivityFilter
            window:activityName=".ExpandedActivity"/>
    </ActivityRule>

</resources>

创建初始化程序。

WindowManager RuleController 组件会解析 XML 配置文件,并将规则提供给系统。Jetpack Startup 库 Initializer 会在应用启动时为 RuleController 提供 XML 文件,以便在任何 activity 启动时,这些规则都会生效。

如需创建初始化程序,请执行以下操作:

将最新的 Jetpack Startup 库依赖项添加到模块级 build.gradle 文件中,例如:

implementation 'androidx.startup:startup-runtime:1.1.1'

注意:如需了解发布版本,请参阅 Startup 版本说明。
创建一个实现 Initializer 接口的类。

初始化程序会通过将 XML 配置文件 (main_split_config.xml) 的 ID 传递给 RuleController.parseRules() 方法,将分屏规则提供给 RuleController。

Kotlin

class SplitInitializer : Initializer<RuleController> {

 override fun create(context: Context): RuleController {
     return RuleController.getInstance(context).apply {
         setRules(RuleController.parseRules(context, R.xml.main_split_config))
     }
 }

 override fun dependencies(): List<Class<out Initializer<*>>> {
     return emptyList()
 }
}

Java

public class SplitInitializer implements Initializer<RuleController> {

  @NonNull
  @Override
  public RuleController create(@NonNull Context context) {
      RuleController ruleController = RuleController.getInstance(context);
      ruleController.setRules(
          RuleController.parseRules(context, R.xml.main_split_config)
      );
      return ruleController;
  }

  @NonNull
  @Override
  public List<Class<? extends Initializer<?>>> dependencies() {
      return Collections.emptyList();
  }
}

为规则定义创建 content provider。

将 androidx.startup.InitializationProvider 作为 添加到应用清单文件中。添加对 RuleController 初始化程序实现 SplitInitializer 的引用:

<!-- AndroidManifest.xml -->

<provider android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="merge">
    <!-- Make SplitInitializer discoverable by InitializationProvider. -->
    <meta-data android:name="${applicationId}.SplitInitializer"
        android:value="androidx.startup" />
</provider>

InitializationProvider 会在调用应用的 onCreate() 方法之前发现并初始化 SplitInitializer。因此,分屏规则会在应用的主要 activity 启动时生效。

WindowManager API

您可以通过一些 API 调用程序化地实现 activity 嵌入。在 Application 的子类的 onCreate() 方法中进行调用,确保这些规则在任何 activity 启动之前生效。

注意:对于简单实现,您可以将规则添加到应用主要 activity 或分屏主要 activity 的 onCreate() 方法的 RuleController 中。不过,深层链接以及各种应用销毁和重新创建用例可以绕过以这种方式实现的初始化。为了在所有用例中实现可靠的初始化,请将规则添加到 Application 子类中的 RuleController(如果您使用的是 XML 配置文件,则添加到 Jetpack Startup 初始化程序,详情请参阅 XML 配置)。
如需程序化地创建 activity 分屏,请执行以下操作:

创建分屏规则:

创建一个 SplitPairFilter,用于标识共享分屏的 activity:

Kotlin

val splitPairFilter = SplitPairFilter(
   ComponentName(this, ListActivity::class.java),
   ComponentName(this, DetailActivity::class.java),
   null
)

Java

SplitPairFilter splitPairFilter = new SplitPairFilter(
   new ComponentName(this, ListActivity.class),
   new ComponentName(this, DetailActivity.class),
   null
);

将过滤条件添加到过滤条件集:

Kotlin

val filterSet = setOf(splitPairFilter)

Java

Set<SplitPairFilter> filterSet = new HashSet<>();
filterSet.add(splitPairFilter);

为分屏创建布局属性:

Kotlin

val splitAttributes: SplitAttributes = SplitAttributes.Builder()
    .setSplitType(SplitAttributes.SplitType.ratio(0.33f))
    .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
    .build()

Java

final SplitAttributes splitAttributes = new SplitAttributes.Builder()
      .setSplitType(SplitAttributes.SplitType.ratio(0.33f))
      .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
      .build();

SplitAttributes.Builder 会创建一个包含布局属性的对象:

setSplitType:定义将可用显示区域分配给每个 activity 容器的方式。宽高比分屏类型指定分配给主要容器的可用显示区域比例;辅助容器则会占据剩余的可用显示区域。
setLayoutDirection:指定 activity 容器相对于另一种容器的布局方式,主要容器优先。
构建 SplitPairRule:

Kotlin

val splitPairRule = SplitPairRule.Builder(filterSet)
    .setDefaultSplitAttributes(splitAttributes)
    .setMinWidthDp(840)
    .setMinSmallestWidthDp(600)
    .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f))
    .setFinishPrimaryWithSecondary(SplitRule.FinishBehavior.NEVER)
    .setFinishSecondaryWithPrimary(SplitRule.FinishBehavior.ALWAYS)
    .setClearTop(false)
    .build()

Java

SplitPairRule splitPairRule = new SplitPairRule.Builder(filterSet)
    .setDefaultSplitAttributes(splitAttributes)
    .setMinWidthDp(840)
    .setMinSmallestWidthDp(600)
    .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f))
    .setFinishPrimaryWithSecondary(SplitRule.FinishBehavior.NEVER)
    .setFinishSecondaryWithPrimary(SplitRule.FinishBehavior.ALWAYS)
    .setClearTop(false)
    .build();

SplitPairRule.Builder 会创建并配置规则:

filterSet:包含分屏对过滤条件,通过确定共享分屏的 activity 以确定何时应用规则。
setDefaultSplitAttributes:将布局属性应用于规则。
setMinWidthDp:设置支持分屏的最小显示宽度(以密度无关像素 dp 为单位)。
setMinSmallestWidthDp:设置支持分屏的最小值(以 dp 为单位),无论设备显示方向如何,都必须确保两个显示屏尺寸中较小的尺寸不低于该值才允许分屏。
setMaxAspectRatioInPortrait:设置在纵向模式下显示 activity 分屏的最大宽高比(高度:宽度)。如果纵向模式显示屏的宽高比超过最大宽高比,则无论显示屏的宽度如何,都会停用分屏。注意:默认值为 1.4,这会使 activity 在大多数平板电脑上以纵向模式占据整个任务窗口。另请参阅 SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT 和 setMaxAspectRatioInLandscape。横向的默认值为 ALWAYS_ALLOW。
setFinishPrimaryWithSecondary:设置结束辅助容器中的所有 activity 会对主要容器中的 activity 有何影响。NEVER 表示在辅助容器中的所有 activity 均结束时,系统不应结束主要 activity(请参阅结束 activity)。
setFinishSecondaryWithPrimary:设置结束主要容器中的所有 activity 会对辅助容器中的 activity 有何影响。ALWAYS 表示当主要容器中的所有 activity 均结束时,系统应始终结束辅助容器中的 activity(请参阅结束 activity)。
setClearTop:指定在辅助容器中启动新 activity 时,是否结束该容器中的所有 activity。false 表示新 activity 会堆叠在辅助容器中已有的 activity 之上。
获取 WindowManager RuleController 的单例实例并添加规则:

Kotlin

val ruleController = RuleController.getInstance(this)
ruleController.addRule(splitPairRule)

Java

RuleController ruleController = RuleController.getInstance(this);
ruleController.addRule(splitPairRule);

当内容不可用时,为辅助容器创建占位符:

创建一个 ActivityFilter,用于标识哪个 activity 会与占位符共享任务窗口分屏:

Kotlin

val placeholderActivityFilter = ActivityFilter(
    ComponentName(this, ListActivity::class.java),
    null
)

Java

ActivityFilter placeholderActivityFilter = new ActivityFilter(
    new ComponentName(this, ListActivity.class),
    null
);

将过滤条件添加到过滤条件集:

Kotlin

val placeholderActivityFilterSet = setOf(placeholderActivityFilter)

Java

Set<ActivityFilter> placeholderActivityFilterSet = new HashSet<>();
placeholderActivityFilterSet.add(placeholderActivityFilter);

创建 SplitPlaceholderRule:

Kotlin

val splitPlaceholderRule = SplitPlaceholderRule.Builder(
      placeholderActivityFilterSet,
      Intent(context, PlaceholderActivity::class.java)
    ).setDefaultSplitAttributes(splitAttributes)
     .setMinWidthDp(840)
     .setMinSmallestWidthDp(600)
     .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f))
     .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ALWAYS)
     .setSticky(false)
     .build()

Java

SplitPlaceholderRule splitPlaceholderRule = new SplitPlaceholderRule.Builder(
      placeholderActivityFilterSet,
      new Intent(context, PlaceholderActivity.class)
    ).setDefaultSplitAttributes(splitAttributes)
     .setMinWidthDp(840)
     .setMinSmallestWidthDp(600)
     .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f))
     .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ALWAYS)
     .setSticky(false)
     .build();

SplitPlaceholderRule.Builder 会创建并配置规则:

placeholderActivityFilterSet:包含 activity 过滤条件,通过确定与占位符 activity 关联的 activity 来确定应用规则的时机。
Intent:指定占位符 activity 的启动。
setDefaultSplitAttributes:将布局属性应用于规则。
setMinWidthDp:设置允许分屏的最小显示宽度(以密度无关像素 dp 为单位)。
setMinSmallestWidthDp:设置允许分屏的最小值(以 dp 为单位),无论设备显示方向如何,都必须确保两个显示屏尺寸中较小的尺寸不低于该值才允许分屏。
setMaxAspectRatioInPortrait:设置在纵向模式下显示 activity 分屏的最大宽高比(高度:宽度)。注意:默认值为 1.4,这会使 activity 在大多数平板电脑上以纵向模式填满任务窗口。另请参阅 SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT 和 setMaxAspectRatioInLandscape。横向的默认值为 ALWAYS_ALLOW。
setFinishPrimaryWithPlaceholder:设置结束占位符 activity 会对主要容器中的 activity 有何影响。ALWAYS 表示在占位符结束时,系统应始终结束主要容器中的 activity(请参阅结束 activity)。
setSticky:用于确定当占位符首次出现在具有足够最小宽度的分屏后,占位符 activity 是否要显示在小显示屏上的 activity 堆栈顶部。
向 WindowManager RuleController 添加规则:

Kotlin

ruleController.addRule(splitPlaceholderRule)

Java

ruleController.addRule(splitPlaceholderRule);

注意:RuleController 已针对上一条规则进行实例化。
指定绝不应属于分屏的 activity:

创建一个 ActivityFilter,用于标识始终应占据整个任务显示区域的 activity:

Kotlin

val expandedActivityFilter = ActivityFilter(
  ComponentName(this, ExpandedActivity::class.java),
  null
)

Java

ActivityFilter expandedActivityFilter = new ActivityFilter(
  new ComponentName(this, ExpandedActivity.class),
  null
);

将过滤条件添加到过滤条件集:

Kotlin

val expandedActivityFilterSet = setOf(expandedActivityFilter)

Java

Set<ActivityFilter> expandedActivityFilterSet = new HashSet<>();
expandedActivityFilterSet.add(expandedActivityFilter);

创建 ActivityRule:

Kotlin

val activityRule = ActivityRule.Builder(expandedActivityFilterSet)
    .setAlwaysExpand(true)
    .build()

Java

ActivityRule activityRule = new ActivityRule.Builder(
    expandedActivityFilterSet
).setAlwaysExpand(true)
 .build();

ActivityRule.Builder 会创建并配置规则:

expandedActivityFilterSet:包含 activity 过滤条件,通过确定您希望从分屏中排除的 activity,以确定何时应用规则。
setAlwaysExpand:指定 activity 是否应填充整个任务窗口。
向 WindowManager RuleController 添加规则:

Kotlin

ruleController.addRule(activityRule)

Java

ruleController.addRule(activityRule);

注意:RuleController 之前已实例化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

五一编程

程序之路有我与你同行

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

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

打赏作者

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

抵扣说明:

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

余额充值