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

谷歌在开发者网站已经给出 折叠屏适配指导, 本文将参考官方适配文档简单介绍适配方法。
在app/build.gradle中添加Jetpack WindowManager库 androidx.window:window-java的依赖
build.gradle
dependencies { ... implementation 'androidx.window:window-java:1.1.0-alpha04' ... }
-
FoldingFeature
在折叠屏状态变更时,框架会通知到应用最新的 DisplayFeature 信息。FoldingFeature 是 DisplayFeature 的一种类型,为开发者提供了折叠设备状态信息,包括以下属性:
-
state:设备的折叠状态
展开状态——FLAT
半展开(折叠)状态——HALF_OPENED
如果是完全折叠为小屏,那么此时框架回传的feature列表中是没有FoldingFeature的
-
orientation:折叠边的方向
竖直方向——VERTICAL,此时折叠边属于竖屏,应用可采用左右式布局
水平方向——HORIZONTAL,此时折叠边属于横屏,应用可采用上下式布局
-
occlusionType:折叠边的遮挡类型
折叠边没有遮住屏幕——NONE
折叠边遮住了部分屏幕——FULL
-
isSeparating: 屏幕逻辑显示区域是否分离
双屏幕模式——true
state 为 HALF_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的适配之后的场景

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,那么这是一个双屏的设备。 我们需要参考折叠边的方向 HORIZONTAL、 VERTICAL 进行布局
private boolean isSeparating(FoldingFeature foldFeature) {
return (foldFeature != null) &&
(foldFeature.getState() == FoldingFeature.State.FLAT) &&
(foldFeature.isSeparating() == true);
}