小米开发平台Activity Embedding 适配指南

为了利用大屏幕的显示区域,Google在Jetpack WindowManager中添加了Activity Embedding,实现同一应用内不同activity分屏显示,

小米开发平台Activity Embedding 适配指南

1、功能介绍

为了利用大屏幕的显示区域,GoogleJetpack WindowManager中添加了Activity Embedding,实现同一应用内不同activity分屏显示,开发者可以通过配置XML文件或者进行Jectpack WindowManager API调用确定如何显示activity(并排或堆叠)。

目前支持以下两种逻辑跳转,第一种是可以实现左容器界面固定:

小米开发平台Activity Embedding 适配指南

第二种是支持多层次导航:

小米开发平台Activity Embedding 适配指南

另外系统会自动维护对小屏幕的显示,当应用在小屏幕的设备上时,activity会相互堆叠,在大屏幕上,activity会并排显示,对于折叠屏,会随着设备折叠和展开而堆叠和并排显示activity。

小米开发平台Activity Embedding 适配指南

2、快速上手

JetPack WindowManager库添加了ActivityEmbeddingComponent,可以根据分屏规则创建容器实现应用内分屏,配置分屏规则涉及到下面几个步骤:

2.1 添加依赖项

将WindowManager库依赖项添加到应用的build.gradle文件中

implementation("androidx.window:window:1.1.0-alpha02")

2.2 配置分屏规则

2.2.1 XML静态配置

放在res资源文件的xml文件夹下

    <!-- The split configuration for activities. -->
    
    <resources
      xmlns:window="http://schemas.android.com/apk/res-auto">
      <!-- Automatically split the following activity pairs. -->
      <SplitPairRule
        window:clearTop="true"
        window:splitMinWidth="600dp">
        <SplitPairFilter
          window:primaryActivityName=".SplitActivityList"
          window:secondaryActivityName=".*"/>
      </SplitPairRule>
    
      <!-- Automatically launch a placeholder for the list activity. -->
    
      <SplitPlaceholderRule
        window:placeholderActivityName=".SplitActivityListPlaceholder"
        window:splitRatio="0.3"
        window:splitMinWidth="600dp">
        <ActivityFilter
          window:activityName=".SplitActivityList"/>
      </SplitPlaceholderRule>    
      
    <!-- Automatically launch a full activity. -->
    
    <ActivityRule window:alwaysExpand="true">
        <ActivityFilter 
            window:activityName=".FullActivity"/>
    </ActivityRule>
    
    </resources>

2.2.2 代码动态配置

运行时定义分屏的配置,开发者可以在startActivity或者onCreate时使用

splitController.registerRule(new SplitPairRule(newFilters));

splitController.unRegisterRule(new SplitPairRule(newFilters));

来动态添加/移除规则

protected void onCreate(@Nullable Bundle savedInstanceState) {
    Set<SplitPairFilter> pairFilters = new HashSet<>();
    SplitPairFilter filter = new SplitPairFilter(primaryActivityComponetName, 
            secondaryActivityComponetName, 
            null);
    pairFilters.add(filter);
    SplitPairRule pairRule = new SplitPairRule(pairFilters, 
            SplitRule.FINISH_ADJACENT, 
            SplitRule.FINISH_ALWAYS, 
            true, 
            600, 
            600, 
            0.3f, 
            LayoutDirection.LOCALE);
    SplitController splitController = SplitController.getInstance();
    splitController.registerRule(pairRule);
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

2.3 将规则定义通知库

使用Jetpack Startup库在加载应用的其他组件和启动 activity 之前执行初始化。如需启用启动功能,请在应用的 build 文件中添加库依赖项:

implementation("androidx.startup:startup-runtime:1.1.0")

并在manifest中添加:

<!-- AndroidManifest.xml -->
<provider android:name="androidx.startup.InitializationProvider"
  android:authorities="${applicationId}.androidx-startup"
  android:exported="false"
  tools:node="merge">
  <!-- This entry makes ExampleWindowInitializer discoverable. -->
  <meta-data android:name="**androidx.window.sample.embedding.ExampleWindowInitializer**"
    android:value="androidx.startup" />
</provider>

2.4 添加初始化程序类实现

通过将包含定义 (main_split_config) 的 xml 资源文件的 ID 提供给 SplitController.initialize() 来设置规则:

class ExampleWindowInitializer extends Initializer<SplitController> {

  @Override
  SplitController create(Context context) {
    SplitController.initialize(context, R.xml.main_split_config);
    return SplitController.getInstance(context);
  }

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

}

2.5 XML配置文件参数解析

参数含义
SplitPairRule分屏配对情景(两侧容器均有具体activity),对应SplitPairFilter
splitRatio分屏比默认为0.5f,即左右5:5分屏 ,对于IM类应用,可考虑设置分屏比为0.3f,即左右3:7分屏
splitMinWidth默认配置600dp,宽度达到600dp才可以分屏,主窗口可分屏显示的最小窗口宽度
splitMinSmallestWidth默认配置600dp,主窗口可分屏显示的最小sw值
finishPrimaryWithSecondary默认为  false,true:若secondary container中所有activity都finish,则primary container中创建分屏的activity也会finish,不推荐应用主动配置此项。(androidx.window:window:1.1.0-alpha03中默认是SplitRule.FINISH_ADJACENT)
finishSecondaryWithPrimary默认为 ture, true:若primary container中所有activity都finish,则secondary container中所有activity也会finish,不推荐应用主动配置此项。(androidx.window:window:1.1.0-alpha03中默认是SplitRule.FINISH_ALWAYS)
clearTop默认为 false,true:启动activity窗口分屏,存在相同的primary container,若新建secondary container,则原secondary container中的activity会被finish掉,推荐应用配置,避免右分屏出现多实例
SplitPairFilter分屏配对关系(必需配置)
primaryActivityName分屏的primay activity component name
secondaryActivityName分屏的secondary activity component name
secondaryActivityAction分屏的secondary activity 启动的action (配置此项需要在启动的时候添加action) 
参数含义
SplitPlaceholderRule用来描述分屏下的占位,对应ActivityFilter(必需配置)
placeholderActivityIntentName通过同时打开两个activity创建分屏,secondary占位activity component name
componentName组件名
intentActionintent
splitRatio分屏比默认为0.5f,即左右5:5分屏
splitMinWidth默认配置600dp,宽度达到600dp才可以分屏主窗口可分屏显示的最小窗口宽度
splitMinSmallestWidth默认配置600dp,主窗口可分屏显示的最小sw值
ActivityFilter适配占位规则的activity(必需配置)
参数含义
ActivityRule需要全屏显示的activity,对应ActivityFilter(必需配置
alwaysExpand取值”true”或false true:启动的activity全屏显示
componentName组件名
intentActionintent
ActivityFilter适配全屏规则的activity(必需配置)

2.6 应用主动适配生效

2.6.1 MIUI meta-data标记

AndroidManifest.xml中声明下列meta-data字段。设置为true表示app已经主动适配,系统不会反向适配

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
    <meta-data android:name="embedded" android:value="true"/>
</application>

注意:

Android 13后该meta-data将不使用

2.6.2 Google property标记

Google官方添加property字段,用来标识APP是否允许系统反向适配Activity Embedding

true表示允许系统反向适配,false不允许系统反向适配

注意:自适配Activity Embedding和不允许系统反向适配Activity Embedding的APP需要设置为false

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

3、适配指导

3.1 关于应用横竖屏

3.1.1 PAD 竖屏下进入ActivityEmbedding

平板设备处于竖屏状态时,我们推荐应用采用左下图的全屏显示样式,而不是进入ActivityEmbedding。竖屏下的左右分屏显示会导致信息量过多,难以聚焦。如果您的应用在竖屏下进入了ActivityEmbedding,原因是:应用设置的进入ActivityEmbedding的阈值splitMinWidth过小

小米开发平台Activity Embedding 适配指南
  • 获取屏幕大小

R版本之前:Display.getRealSize()、Display.getRealMetrics()。

R版本之后:WindowManager.getMaximumWindowMetrics() 。Jetpack WindowManager中支持使用WindowMetrics maximumWindowMetrics = WindowMetricsCalculator.

getOrCreate().computeMaximumWindowMetrics(activity);

  • 获取当前窗口大小

R版本之前: Display.getSize() 。

R版本之后:WindowManager.getCurrentWindowMetrics() 。Jetpack WindowManager中支持使用WindowMetrics maximumWindowMetrics = WindowMetricsCalculator.

getOrCreate(). computeCurrentWindowMetrics(activity);

推荐解决方案:开发者根据不同屏幕大小的设备动态计算进入分屏的阈值。可使用如下计算方式:

// 获取屏幕大小
WindowMetrics currentWindowMetrics = WindowMetricsCalculator.getOrCreate().
        computeCurrentWindowMetrics(activity);
// 获取当前窗口大小
WindowMetrics maximumWindowMetrics = WindowMetricsCalculator.getOrCreate().
        computeMaximumWindowMetrics(activity);
// 通过configuration获取desity
float density = (float) activity.getResources().getConfiguration().densityDpi / 160f;
// 根据屏幕大小动态设置window:splitMinWidth="600dp"大小,控制竖屏下不进ActivtiyEmbedding
// 为了保证折叠屏外屏下不进入ActivityEmbedding,需要设置一下splitMinSmallestWidth的值,默认600dp
int splitMinWidth = (int) (Math.min(maximumWindowMetrics.getBounds().width(), 
        maximumWindowMetrics.getBounds().height()) / density + 1);

3.1.2 PAD无法横屏

原因:ActivityEmbedding 只是改变activity的显示位置,不会强制更改方向,需要app支持横屏

推荐方案:第一个启动的activity方向设置为behind以及全屏启动的activity设置为behind

3.1.3 折叠屏控制在外屏竖屏显示

为了避免您适配ActivityEmbedding后在折叠屏外屏出现如下图所示的情况,需要控制应用在折叠屏外屏竖屏显示,监听 WindowInfoTracker 来手动设置方向,具体使用参考官方文档https://developer.android.com/guide/topics/large-screens/make-apps-fold-aware

小米开发平台Activity Embedding 适配指南

3.2 支持resizeable

应用需要在 AndroidManifest.xml 文件的 application 或者 actvivity 标签中添加 resizeableActivity=true 的属性。

若配置android:resizeableActivity=“false”,会导致无法分屏显示

<application
    android:resizeableActivity="true">
    <activity
          android:resizeableActivity="true" />
</application>

3.3 关于Configuration

当应用在折叠屏内外屏或平板横竖屏交替使用时,activity的大小会发生变化,我们强烈推荐Activity在大小切换时不重启来保证良好的连续性体验,遵循google规范,在android:configChanges 属性增加 screenSize|screenLayout|orientation|smallestScreenSize,并在Activity的onConfigurationChanged回调中更新宽高刷新各个子布局。

3.4 视频全屏

分屏时请求横屏还是在分屏下显示,无法横屏全屏

建议配置activity 始终填满任务窗口:

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

3.5 clearTop标记

若配置为true

启动activity窗口分屏,存在相同的primary container,会创建新的secondary container,原来的secondary container中的activity会被finish掉,避免右分屏出现多实例。开发者可以根据需要对单独的SplitPairRule 进行配置clearTop=”true”。

小米开发平台Activity Embedding 适配指南

用户折叠手机时,屏幕 B 在屏幕 A 之上,屏幕 A 又在菜单之上。当用户从屏幕 B 进行返回导航时,系统会显示屏幕 A 而不是Menu。

可以将分屏配置为通过 clearTop 清除之前的辅助容器,并正常启动新的 activity。

<SplitPairRule
    window:clearTop="true">
    <SplitPairFilter
        window:primaryActivityName=".Menu"
        window:secondaryActivityName=".ScreenA"/>
    <SplitPairFilter
        window:primaryActivityName=".Menu"
        window:secondaryActivityName=".ScreenB"/>
</SplitPairRule>

3.6 new Task与Launch Flag

当分屏任务窗口中的 activity 启动新任务中的 activity 时,新任务将与包含分屏的任务分开并显示在全窗口中。

在分屏任务窗口中,Launch flag同样起作用,可以复用activity。

小米开发平台Activity Embedding 适配指南

3.7 多进程规则匹配

问题:同一Task任务下,子进程配置activity成全屏不生效

小米开发平台Activity Embedding 适配指南

采用Jetpack Startup不指定android:process时默认在主进程初始化SplitController,在xml文件夹中的分屏规则不会在子进程生效

<!-- AndroidManifest.xml -->
<provider android:name="androidx.startup.InitializationProvider"
  android:authorities="${applicationId}.androidx-startup"
  android:exported="false"
  tools:node="merge">
  <!-- This entry makes ExampleWindowInitializer discoverable. -->
  <meta-data android:name="**androidx.window.sample.embedding.ExampleWindowInitializer**"
    android:value="androidx.startup" />
</provider>

目前解决方案是需要重写Application在onCreate方法中对需要规则匹配的进程初始化SplitController并且加载静态规则。

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        SplitController.initialize(getApplicationContext(), R.xml.split_config);
        super.onCreate();
    }
}

3.8 响应分屏状态变化

ActivityEmbedding下如何判断activity是否处于分屏以及window mode变化,如何实时响应分屏状态变化。

3.8.1 判断是否在左右分屏

左右分屏下,activity中的configuration的mode是multi-window(与系统分屏下的mode一样,在进行部分业务处理时需要注意)

全屏状态下,activity中的configuration的mode是fullscreen

判断当前activity是否在左右分屏状态,可以通过SplitController.getInstance().isActivityEmbedded(Activity activity)进行区分,返回true表示处于分屏。

3.8.2 如何响应分屏状态变化

为了知道 activity 何时在分屏中,可以向SplitController注册一个监听器来监听分屏状态的变化。然后,相应地调整界面:

小米开发平台Activity Embedding 适配指南
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    splitController
        .addSplitListener(this, mainThreadExecutor, SplitInfoChangeCallback());
}

class SplitInfoChangeCallback extends Consumer<List<SplitInfo>> {
    public void accept(List<SplitInfo> splitInfoList) {
        findViewById<View>(R.id.infoButton).visibility =
            !splitInfoList.isEmpty()) ? View.GONE : View.VISIBLE;
    }
}

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

3.9 占位Activity Placeholder

小米开发平台Activity Embedding 适配指南

如需创建带有占位符的分屏,请创建一个占位符并将其与主要 activity 相关联:

<SplitPlaceholderRule  
window:placeholderIntentName=".Placeholder">  
<ActivityFilter    
    window:activityName=".Main"/>
</SplitPlaceholderRule>

注意:开发者不需要自己启动placeholder,会创建多个TaskFragment导致启动异常,在真正显示主页面时建议不启动placeholder,可以动态注册规则,比如在进入主界面前先跳转到登录界面,如果启动了placeholder有可能会导致异常。

编辑:yimen,如若转载,请注明出处:https://www.yimenapp.com/kb-yimen/12658/

部分内容来自网络投稿,如有侵权联系立删

(2)
上一篇 2022年11月24日 下午3:01
下一篇 2022年11月24日 下午3:04

相关推荐