悬停模式适配指导 - finalxiaoxiao/study GitHub Wiki

悬停模式适配指导

Table of Contents

近年来各大厂商陆续推出可折叠设备,因此需要广大应用开发者适配折叠模式,得以让App具有更加丰富的功能和交互, 让用户拥有更好的应用使用体验。

foldable_multiple_postures.png

谷歌在开发者网站已经给出 折叠屏适配指导, 本文将参考官方适配文档简单介绍适配方法。

在app/build.gradle中添加Jetpack WindowManager库 androidx.window:window-java的依赖

build.gradle

dependencies {
    ...
    implementation 'androidx.window:window-java:1.1.0-alpha04'
    ...
}
  • FoldingFeature

    在折叠屏状态变更时,框架会通知到应用最新的 DisplayFeature 信息。FoldingFeatureDisplayFeature 的一种类型,为开发者提供了折叠设备状态信息,包括以下属性:

    • state:设备的折叠状态

      展开状态——FLAT

      半展开(折叠)状态——HALF_OPENED

      如果是完全折叠为小屏,那么此时框架回传的feature列表中是没有FoldingFeature的

    • orientation:折叠边的方向

      竖直方向——VERTICAL,此时折叠边属于竖屏,应用可采用左右式布局

      水平方向——HORIZONTAL,此时折叠边属于横屏,应用可采用上下式布局

    • occlusionType:折叠边的遮挡类型

      折叠边没有遮住屏幕——NONE

      折叠边遮住了部分屏幕——FULL

    • isSeparating: 屏幕逻辑显示区域是否分离

      双屏幕模式——true

      stateHALF_OPENED——true

      其他——false

首先,我们需要创建一个Callback用于接收折叠屏状态的变更通知。

public class SplitLayoutActivity extends AppCompatActivity {
    ...
    private final Consumer<WindowLayoutInfo> layoutStateChangeCallback = newLayoutInfo -> {
        List<DisplayFeature> featureList = newLayoutInfo.getDisplayFeatures();
        for (DisplayFeature feature : featureList) {
            if (feature instanceof FoldingFeature) {
                FoldingFeature foldingFeature = (FoldingFeature) feature;
                FoldingFeature.Orientation orientation = foldingFeature.getOrientation();
                FoldingFeature.State state = foldingFeature.getState();
                FoldingFeature.OcclusionType occlusionType = foldingFeature.getOcclusionType();
                boolean separating = foldingFeature.isSeparating();
                // Decide if us need to change the layout
            }
            // Here are the layout suggestions given by Google
            if (feature instanceof FoldingFeature) {
                if (isTableTopPosture((FoldingFeature) feature)) {
                    enterTabletopMode(feature);
                } else if (isBookPosture((FoldingFeature) feature)) {
                    enterBookMode(feature);
                } else if (isSeparating((FoldingFeature) feature)) {
                    // Dual-screen device
                    if (((FoldingFeature) feature).getOrientation() ==
                            FoldingFeature.Orientation.HORIZONTAL) {
                        enterTabletopMode(feature);
                    } else {
                        enterBookMode(feature);
                    }
                } else {
                    enterNormalMode();
                }
            }
        }
        SplitLayoutActivity.this.runOnUiThread(() -> {
            // Use newLayoutInfo to update the layout.
        });
    };
    ...
}

然后,我们可以在应用进入前台时注册回调,在应用退出后台时取消注册。

public class SplitLayoutActivity extends AppCompatActivity {
    ...
    private WindowInfoTrackerCallbackAdapter windowInfoTracker;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        windowInfoTracker = new WindowInfoTrackerCallbackAdapter(
                WindowInfoTracker.getOrCreate(this));
        ...
    }
    @Override
    protected void onStart() {
        ...
        windowInfoTracker.addWindowLayoutInfoListener(
                this, Runnable::run, layoutStateChangeCallback);
    }
    @Override
    protected void onStop() {
        ...
        windowInfoTracker.removeWindowLayoutInfoListener(
            layoutStateChangeCallback);
    }
}

当折叠设备处于折叠状态,并且折叠边是水平的,那么用户通常正将折叠屏置于桌面之上。 例如 Google DUO的适配之后的场景

duo-before-after-tabletop.png
private boolean isTableTopPosture(FoldingFeature foldFeature) {
    return (foldFeature != null) &&
           (foldFeature.getState() == FoldingFeature.State.HALF_OPENED) &&
           (foldFeature.getOrientation() == FoldingFeature.Orientation.HORIZONTAL);
}

当折叠设备处于折叠状态,并且折叠边是垂直的,那么设备通常是以图书的方式展开,因此我们需要使用左右式的布局。

private boolean isBookPosture(FoldingFeature foldFeature) {
    return (foldFeature != null) &&
           (foldFeature.getState() == FoldingFeature.State.HALF_OPENED) &&
           (foldFeature.getOrientation() == FoldingFeature.Orientation.VERTICAL);
}

当折叠屏设备处于 FLAT 并且 isSeparating 是true,那么这是一个双屏的设备。 我们需要参考折叠边的方向 HORIZONTALVERTICAL 进行布局

private boolean isSeparating(FoldingFeature foldFeature) {
    return (foldFeature != null) &&
           (foldFeature.getState() == FoldingFeature.State.FLAT) &&
           (foldFeature.isSeparating() == true);
}
⚠️ **GitHub.com Fallback** ⚠️