Android折叠屏适配攻略

原文地址:https://juejin.cn/user/627178281904907/posts

各个厂商都在发布折叠屏手机,整体折叠屏手机的占有率越来越高。基于此我们于去年推进了折叠屏的适配,本篇整体梳理一下在适配折叠屏过程中,我们做了哪些事情,遇到了哪些坑点。

/   Activity Embedding   /

首先在 Android 12L 及以上的版本,推荐使用 Google 官方的折叠屏适配方案。即 Activity Embedding。目前用于适配折叠屏的 window 库已经升级到1.3.0版,androidx.window:window:1.3.0,我们在适配时还是 1.1.0-alpha03。接下来简单介绍一下具体的适配步骤。

配置

使用 Activity 嵌入需要引入两个库:

implementation androidx.window:window:1.3.0
implementation androidx.startup:startup-runtime:1.1.1

如果是使用最新的 window 库需要添加以下配置:

<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>

官方的描述是:

将 android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED 属性添加到应用清单文件中的元素,然后将该值设置为 true。在 WindowManager 版本 1.1.0-alpha06 及更高版本中,除非将该属性添加到清单中并设置为 true,否则系统会停用 activity 嵌入分屏。

由于笔者的工程中使用的是 1.1.0-alpha03 版本,所以我们工程是没有添加这个配置的。

使用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>

具体的字段含义笔者在这里就不详细描述了,否则就和关闭官方文档没有区别了。具体详细的配置大家可以看官方的指引。

若要使 config 配置生效需要利用启动库添加配置。在笔者的适配过程中我们利用这个功能分别配置了针对折叠屏与平板的适配:

public class CustomWindowInitializer implements Initializer<SplitController> {

    @NonNull
    @Override
    @androidx.window.core.ExperimentalWindowApi
    public SplitController create(@NonNull Context context) {
        if (DeviceUtils.isTabletDevice()) {
            SplitController.initialize(context, R.xml.split_config_tablet);
        } else {
            SplitController.initialize(context, R.xml.split_config);
        }
        return SplitController.getInstance();
    }

    @NonNull
    @Override
    public List<Class<? extends Initializer<?>>> dependencies() {
        return new ArrayList<>();
    }
}

之所以这样配置,是因为我们希望在 Android 平板上也可以有分屏效果。split_config 与 split_config_tablet 两个文件不同的,只有 splitRatio 这个字段。在 split_config 中 splitRatio 为0.5,意思是1:1,在 split_config_tablet 中 splitRatio 为0.33,意味着主副两个屏幕为3:7。

另外就是在 PlaceholderActivity 中,需要自行处理这个 Activity 何时关闭。例如当用户将折叠屏关闭时,这个 PlaceholderActivity 就需要被关闭了。可以在 PlaceholderActivity 中重写 onConfigurationChanged 方法来触发检测,然后调用 SplitController.getInstance().isActivityEmbedded(activity); 接口来判断当前是否为折叠屏状态。

/   各个厂商的适配   /

在上一个章节简单介绍了如何利用 Google 官方的配置来利用 Activity 嵌入的方式适配折叠屏,不过官方仅支持 Android12L 及以上的用户,在 Android12 以下上面的配置就不生效了。

不过国内的各个厂商早在 Google 官方出解决方案之前就输出了厂商上适配方案(不知道 Google 设计方案时,是否参考了国内厂商的方案),在12及以下我们可以使用厂商的适配方案。

各个厂商的适配方案有些不同,这里我们区分不同的厂商介绍。

华为

华为官方称呼这套适配方案为平行视界。需要先在清单文件中添加配置:

<meta-data android:name="EasyGoClient" android:value="true" />

然后在 “assets” 目录下新建配置文件 “easygo.json”。

{
  "easyGoVersion": "1.0",
  "client": "com.huawei.example",
  "logicEntities": [
    {
      "head": {
        "function": "magicwindow",
        "required": "true"
      },
      "body": {
        "mode": "0",
        "defaultDualActivities": {
          "mainPages": "com.huawei.example.Main1Activity",
          "relatedPage": "com.huawei.example.A0Activity"
        },
        "transActivities": [
          "com.huawei.example.A1Activity",
          "com.huawei.example.A2Activity"
        ],
        "Activities": [
          {
            "name": "com.huawei.example.AFullScreenActivity",
            "defaultFullScreen": "true"
          },
          {
            "name": "com.huawei.example.BFullScreenActivity",
            "lockSide": "primary"
          }
        ],
        "UX": {
          "supportRotationUxCompat": "false",
          "isDraggable": "false",
          "supportDraggingToFullScreen": "PAD|FOLD"
        }
      }
    }
  ]
}

字段的解释可以看官方的文档。需要注意的是在华为手机上适配折叠屏有一些官方没有说明的项需要注意:

  • 添加以上配置后,App 不会立即显示为分屏状态,需要进入系统设置中搜索【应用分栏视图】找到对应的应用,开启开关,这样应用才会有分屏效果

  • 这个开关对于绝大部分应用是关闭的,对于购物类或者白名单应用是默认开启的,例如微信就是默认开启的。所以如果你也想你的 App 是默认开启的,需要联系官方的运营人员沟通


OPPO

OPPO 的 Find N 折叠屏系列也卖的不错,其官方也有输出针对折叠屏的适配。OPPO 称其为平行视窗。不过首先 OPPO 官方或者说整个金标联盟都推荐,在 Android12 以上的设备有限使用 Google 的 Activity 嵌入的方式。然后在低版本再使用厂商的适配规则。

基于此可以在清单文件中添加以下配置,当应用同时接入 Activity Embedding 与 OPPO 自研平行视窗时,系统会根据 ROM 的版本优先支持原生的 Activity Embedding,如果 ROM 版本不支持 Activity Embedding,则支持自研的平行视窗。此时应用需要在 manifest 中添加如下标识,并指定为 false 。

<application>
    <property
      android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE"
      android:value="false" />
</application>

另外有意思的是 OPPO 配置平行视窗的方式与华为的完全一样,这里就不赘述了。

VIVO

vivo 与 OPPO 华为又有一些不同。

需要在 app 的 AndroidManifest.xml 文件中的标签内新增 <meta-data android:name="VivoMultiWindow" android:value="true" />。在 assert 目录下新建配置文件 “vivo_multiwindow.json”,模板示例如下:

{
    "funtouchPlugVersion": "1.1",
    "package": "com.vivo.test",
    "elements": [
        {
            "fun": {
                    "name": "multi-landscape",
                    "enable": "true"
                    },
            "config": {
                    "mode": 1,
                    "action": [
                        {
                            "pages": "com.vivo.test.MainActivity",
                            "attachedPage": "com.vivo.test.primaryActivity1"
                        },
                        {
                            "from": "com.vivo.test.FromActivity1",
                            "to": "com.vivo.test.ToActivity"
                        },
                        {
                            "from": "com.vivo.test.FromActivity2",
                            "to": "*"
                        }
                    ],
                    "Activities": {
                        "fullscreenActivities": [
                            "com.vivo.test.fullScreenActivity1",
                            "com.vivo.test.fullScreenActivity2"
                        ],
                        "transitActivities": [
                            "com.vivo.test.transitionActivity1",
                            "com.vivo.test.transitionActivity2",
                            "com.vivo.test.transitionActivity3"
                            ],
                    },
                    "EX": {
                        "draggable": "true",
                        "statusBarVisible": "true",
                        "videoAutoFullscreen": "true",
                        "isRelaunchForResizing": "false",
                        "supportDoubleResume": "true",
                        "windowsGravity": [
                            {
                                "deviceType": "FOLD",
                                "gravity": "1|2"
                            },
                            {
                                "deviceType": "PAD",
                                "gravity": "900|1654"
                            }
                        ],
                        "splitBarColor": "0xffe5e5e5"
                    }
                }
        }
    ]
}

字段的含义基本与其他几家都差别不大。

小米

在小米的官方文档中没有找到小米自己的适配文档,而是直接介绍了官方的 Activity 嵌入适配方式。

/   其他的坑   /

华为需要自行开启分屏适配

这点前面有介绍过,华为需要用户自行在设置中找到【应用分栏视图】设置项,选取对应的 App 进行开始开启。初期由于不清楚,添加了适配逻辑之后一直没有分屏的效果排查了很久,后面翻华为的论坛才看到相关的内容。

华为手机修改配置后最好升级版本号

调整本地分屏配置后,可能会出现不生效的情况,此时可以考虑卸载安装,或者升高 App 的版本号。

一些代码上的逻辑调整

添加分屏逻辑之后,需要整体排查应用中关于页面宽度、坐标点位的逻辑。

举例:

  • 之前是通过获取屏幕宽度作为Activity宽度的逻辑,这种逻辑在折叠屏分屏中数据就不对了。

  • 之前使用MotionEvent#getRowX方法获取坐标点,这里折叠屏需要减掉主屏宽度


判断OPPO折叠屏的方法

public static boolean isOPPOFold() {
    boolean isFold = false;
    try {
        Class<?> cls = Class.forName("com.oplus.content.OplusFeatureConfigManager");
        Method instance = cls.getMethod("getInstance");
        Object configManager = instance.invoke(null);
        Method hasFeature = cls.getDeclaredMethod("hasFeature", String.class);
        Object object = hasFeature.invoke(configManager, "oplus.hardware.type.fold");
        if (object instanceof Boolean) {
            isFold = (boolean) object;
        }
    } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
        e.printStackTrace();
    }
    return isFold;
}

小米判断折叠屏

boolean isFoldDisplay = SystemProperties.getInt("persist.sys.muiltdisplay_type", 0) == 2;

vivo判断折叠屏

private static boolean isVivoFoldableDevice(){
    try {
        Class<?>  c= Class.forName("android.util.FtDeviceInfo");
        Method  m = c.getMethod("getDeviceType");
        Object dType = m.invoke(c);
        Log.d("fold","getDeviceType="+dType);
        return "foldable".equals(dType);
    } catch (Exception e) {
        e.printStackTrace();

    }
    return false;
}

关注我获取更多知识或者投稿

a3e6a5ec7af2bef29d77d74e9dd863f1.jpeg

2c0227ed548f270e9b092fbef795a421.jpeg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值