背景
WindowInsets在手势导航中解决视觉冲突和手势冲突,以及挖孔屏适配上都有重要作用。
解决视觉冲突和手势冲突
先贴上Google的几篇连载文章
Google的该系列文章首先讲解了手势导航出现的背景,为了更加沉浸式的体验(edge-to-edge)。技术上引入了边衬区 (Insets)的概念,Insets 区域负责描述屏幕的哪些部分会与系统 UI 相交 (intersect),例如导航或状态栏。使用WindowInsets类来表示,该类包含了五种Insets,分别是:
-
系统窗口边衬区getSystemWindowInsets()
-
可点击区域getTappableElementInsets()
-
系统手势边衬区getSystemGestureInsets()
-
强制系统手势边衬区getMandatorySystemGestureInsets()
-
稳定显示边衬区getStableInsets()
通过这些边衬区的获取来解决手势导航(或者说是实现沉浸式体验要解决的)带来的视图冲突和手势冲突。
挖孔屏适配
android O(8)版本开始有挖孔屏,由于Google没有提供标准的接口查询是否是挖孔屏,所以各厂商的判断方案都不一致(令人头秃),从P(android 9)版本开始标准接口(各厂商已安标准接入),即通过WindowInsets的getDisplayCutout()方法获取挖孔信息。
可在Activity的onAttachedToWindow()的回调中去获取(因为只在view是attach的时候该WindowInsets存在)。
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
// 从RootWindowInsets去获取
WindowInsets rootWindowInsets = getWindow().getDecorView().getRootWindowInsets();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
DisplayCutout displayCutout = rootWindowInsets.getDisplayCutout();
if (displayCutout != null) {
// 可根据屏幕旋转的情况获取对应的值
displayCutout.getSafeInsetTop();
displayCutout.getSafeInsetBottom();
displayCutout.getSafeInsetLeft();
displayCutout.getSafeInsetRight();
}
}
}
也可以主动触发,在回调方法中获取,适用于不一定能覆写onAttachedToWindow()的场景
private void getDisplayCutoutInfo(Activity activity) {
if (activity == null || activity.isDestroyed() || activity.isFinishing()) {
return;
}
final View decorView = activity.getWindow().getDecorView();
// 主动触发onApplyWindowInsets回调
decorView.requestApplyInsets();
decorView.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
@Override
public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
// 注意直接从insets中获取getDisplayCutout()会出现为null现象,导致获取不到挖孔信息
WindowInsets rootWindowInsets = v.getRootWindowInsets();
if (rootWindowInsets != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
DisplayCutout displayCutout = rootWindowInsets.getDisplayCutout();
}
}
// 设置为null可以防止多次回调;如果不设置,应该要保证onApplyWindowInsets回调里面的逻辑具有幂等性
decorView.setOnApplyWindowInsetsListener(null);
return insets;
}
});
}
附上onApplyWindowInsets的调用流程