文章目录

  • Android 11—-瀑布屏初识
    • 声明
    • 瀑布屏简介
    • 瀑布屏系统配置
    • 瀑布屏API接口
      • 关键API
      • layoutInDisplayCutoutMode 窗口属性
      • 官方相关技术文档
    • 瀑布屏应用适配
      • 调试
      • 应用适配瀑布屏
    • 参考资料
    • 修改说明

Android 11—-瀑布屏初识

声明

博客地址:http://blog.csdn.net/luzhenrong45

瀑布屏简介

最近开始适配Android R,因此需要对Android R上的一些新特性功能进行大致了解,其中就有包括Google对瀑布屏的原生支持。瀑布屏手机是指拥有超高曲率的曲面屏手机,一种无边框的全面屏,被视为刘海屏的变体。目前国内华米OV都推出了自家的瀑布屏手机产品,比如华为Mate 40 Pro、vivo NEX 3、小米10等,瀑布屏可以说是2020下半年旗舰手机的标配特性之一了。

瀑布屏系统配置

Android 11 开始官方支持瀑布屏,Google将瀑布屏视为刘海屏的一种变体类型,首先回顾下刘海屏的系统配置方法,Android P版本开始原生支持刘海屏,有以下几种:

  • 顶部中间刘海屏:刘海屏位于顶部边缘的中间位置,这种刘海屏比较常见。
  • 边角刘海屏:刘海屏位于边角处或稍微偏离中心的位置
  • 底部刘海屏:刘海屏位于底部
  • 双刘海屏:一个刘海屏位于顶部,一个位于底部
  • 水滴屏:可以理解为顶部中间刘海屏的一种,只不过刘海区域类似于水滴形状,宽度较窄
  • 打孔屏:在屏幕一角圆孔型挖孔,相机模块位于此处,因此屏幕边缘不会被凹口触及

要在设备上实现刘海屏,一般需要在系统代码配置以下几个值:

配置项 说明
quick_qs_offset_height 定义“快捷设置”面板的上外边距。时钟和电池图标显示在该面板上方的空间。
在 values-land 中,将此值设置为 status_bar_height_landscape,在纵屏模式下,将此值设置为默认值 48dp 或刘海屏高度(以较大者为准)。可以根据需要选择高于刘海屏的高度。

quick_qs_total_height 展开通知栏时,“超快设置”面板(收起的“快捷设置”面板)的总高度(其中包括包含时钟图标的面板上方的空间)。
受“快捷设置”面板的布局方式限制,“超快设置”面板(包括偏移量)的总高度必须是静态已知的,因此必须由同一增量 quick_qs_offset_height 调整此值。在 Values-land 中,此值默认为 152dp,在纵屏模式下,此值默认为 176dp。

status_bar_height_portrait 从框架的角度而言,状态栏的默认高度。
在大多数设备上,此值默认为24dp。如果设备上有刘海屏,则将此值设置为刘海屏的高度。可以根据需要选择高于刘海屏的高度。

status_bar_height_landscape 状态栏在横屏模式下的高度。由于仅支持在设备的短边上显示刘海屏,因此此值始终是未经更改的状态栏高度。
在没有刘海屏的设备上,此值等同于 status_bar_height_portrait。如果设备上有刘海屏,则将此值保留为默认的状态栏高度。

config_mainBuiltInDisplayCutout 用于定义刘海屏形状的路径。这是一个可由 android.util.PathParser 解析的字符串,并且是告知系统刘海屏大小和形状的方式。
可在路径中指定 @dp 以便模拟针对不同设备的形状。由于实际的刘海屏具有精确的像素尺寸,因此在定义硬件刘海屏的路径时,请勿使用 @dp 指定符。

config_fillMainBuiltinDisplayCutout 一个确定是否在软件中绘制刘海屏路径(在上文中进行了定义)的布尔值。可用于模拟刘海屏,或填充实际刘海屏,以实现抗锯齿。如果为 true,则系统会以黑色填充 config_mainBuiltInDisplayCutout。

比如开发者选项中,模拟水滴屏的系统配置:

【frameworks/base/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml】

<string translatable="false" name="config_mainBuiltInDisplayCutout">
    M 0,0
    L -24, 0
    L -21.9940446283, 20.0595537175
    C -21.1582133885, 28.4178661152 -17.2, 32.0 -8.8, 32.0
    L 8.8, 32.0
    C 17.2, 32.0 21.1582133885, 28.4178661152 21.9940446283, 20.0595537175
    L 24, 0
    Z
    @dp
</string>

<string translatable="false" name="config_mainBuiltInDisplayCutoutRectApproximation">@*android:string/config_mainBuiltInDisplayCutout</string>

<!-- Whether the display cutout region of the main built-in display should be forced to black in software (to avoid aliasing or emulate a cutout that is not physically existent). -->
<bool name="config_fillMainBuiltInDisplayCutout">true</bool>

<!-- Height of the status bar -->
<dimen name="status_bar_height_portrait">48dp</dimen>
<dimen name="status_bar_height_landscape">28dp</dimen>
<!-- Height of area above QQS where battery/time go (equal to status bar height if > 48dp) -->
<dimen name="quick_qs_offset_height">48dp</dimen>
<!-- Total height of QQS (quick_qs_offset_height + 128) -->
<dimen name="quick_qs_total_height">176dp</dimen>

另外,为了确保刘海屏不会对应用造成负面影响(比如刘海区域影响了应用内容显示或响应操作等),需要确保:

  • 在竖屏模式下,状态栏的高度至少与刘海屏的高度持平
  • 在全屏模式和横屏模式下,刘海屏区域必须显示遮幅式黑边

刘海屏详细介绍可查阅 Android官方文档

Android 11版本对刘海屏的设备类型支持进行了扩展,开始支持瀑布屏,但截止目前官方文档并没有介绍瀑布屏的具体系统配置方法。通过走读刘海屏(DisplayCutout)相关系统源码,发现Android 11.0在10.0基础上新增一些代码,以实现支持瀑布屏参数的配置和获取。

【frameworks/base/core/java/android/view/DisplayCutout.java】

private static Insets loadWaterfallInset(Resources res) { 
    return Insets.of(
            res.getDimensionPixelSize(R.dimen.waterfall_display_left_edge_size),
            res.getDimensionPixelSize(R.dimen.waterfall_display_top_edge_size),
            res.getDimensionPixelSize(R.dimen.waterfall_display_right_edge_size),
            res.getDimensionPixelSize(R.dimen.waterfall_display_bottom_edge_size));
}

非瀑布屏参数(系统默认配置)

【frameworks/base/core/res/res/values/dimens.xml】

<!-- For Waterfall Display -->
<dimen name="waterfall_display_left_edge_size">0px</dimen>
<dimen name="waterfall_display_top_edge_size">0px</dimen>
<dimen name="waterfall_display_right_edge_size">0px</dimen>
<dimen name="waterfall_display_bottom_edge_size">0px</dimen>

瀑布屏专用的配置参数, 参考如下:

【frameworks/base/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml】

<dimen name="waterfall_display_left_edge_size">20dp</dimen> <!-- 瀑布屏左曲边弧长 -->
<dimen name="waterfall_display_top_edge_size">0dp</dimen>
<dimen name="waterfall_display_right_edge_size">20dp</dimen> <!--瀑布屏右曲边弧长-->
<dimen name="waterfall_display_bottom_edge_size">0dp</dimen>

从字面意义上不难理解这4个配置项分别用于手机屏幕四边的曲边弧长配置。一般来说,瀑布屏手机只需要配置左右曲边弧长,即 R.dimen.waterfall_display_left_edge_size 和 R.dimen.waterfall_display_left_edge_size。(当然,不排除手机厂商后续会发布屏幕四边都是瀑布曲边的手机产品)

配置项 说明
waterfall_display_left_edge_size 瀑布屏左部边缘曲边弧长
waterfall_display_top_edge_size 瀑布屏顶部边缘曲边弧长
waterfall_display_right_edge_size 瀑布屏右部边缘曲边弧长
waterfall_display_bottom_edge_size 瀑布屏底部边缘曲边弧长

瀑布屏API接口

Android 11对于瀑布屏手机,更新了专门的UI优化,以减少屏幕两端的曲面部分对显示画面或操作的影响。此次优化类似于之前Android系统适配刘海屏、挖孔屏的方案,Android通过一个DisplayCutout API来帮助应用识别屏幕上是否有空缺或挖孔,以此来避免元素或控件被遮挡,而这次安卓AAndroid 11 对DisplayCutout API进行了扩展,添加了一个DisplayCutout区域用以标记屏幕曲面部分。

应用可以使用现有的凹口屏 API 来管理挖孔屏幕和瀑布屏幕。还有新的 API 可以让您的应用使用包括边缘在内的整个瀑布屏幕,并通过边衬区 (insets) 来管理边缘附近的互动。使用新的API更新应用后,应用可以选择避开在此区域放置内容。

关键API

刘海屏/瀑布屏一些关键的API如下

【frameworks/base/core/java/android/view/WindowInsets.java】

class WindowInsets { 
    DisplayCutout getDisplayCutout();
}

【frameworks/base/core/java/android/view/DisplayCutout.java】

class DisplayCutout { 
    public int getSafeInsetLeft()
    public int getSafeInsetTop()
    public int getSafeInsetRight()
    public int getSafeInsetBottom()
    public Rect getSafeInsets()
    public @NonNull Rect getBoundingRectLeft()
    public @NonNull Rect getBoundingRectTop()
    public @NonNull Rect getBoundingRectRight()
    public @NonNull Rect getBoundingRectBottom()
    public Rect[] getBoundingRectsAll()
    public List<Rect> getBoundingRects()
    public @NonNull Insets getWaterfallInsets()
}
API 说明
public int getSafeInsetLeft() 返回安全区域距离屏幕左边的距离,单位是 px
public int getSafeInsetTop() 返回安全区域距离屏幕顶部的距离,单位是 px
public int getSafeInsetRight() 返回安全区域距离屏幕右边的距离,单位是 px
public int getSafeInsetBottom() 返回安全区域距离屏幕底部的距离,单位是 px
public Rect getSafeInsets() 返回 Rects 的列表,每个 Rects 都是屏幕上非功能区域的边界矩形。
public @NonNull Rect getBoundingRectLeft() 返回位于屏幕左侧边缘非功能区域的矩形边框。
public @NonNull Rect getBoundingRectTop() 返回位于屏幕顶部边缘非功能区域的矩形边框。
public @NonNull Rect getBoundingRectRight() 返回位于屏幕右侧边缘非功能区域的矩形边框。
public @NonNull Rect getBoundingRectBottom() 返回位于屏幕底部边缘非功能区域的矩形边框。
public Rect[] getBoundingRectsAll() 返回一个矩形数组,每个子项是各个边缘非功能区域的矩形边框
public List getBoundingRects() 返回一个矩形列表,每个矩形是显示的非功能区域的边界矩形。
public @NonNull Insets getWaterfallInsets 返回代表瀑布屏弧形区域的边衬区(Insets),Android 11新增API,专门针对瀑布屏设备类型。

【frameworks/base/graphics/java/android/graphics/Insets.java】

class Insets { 
    private Insets(int left, int top, int right, int bottom) { 
        this.left = left;
        this.top = top;
        this.right = right;
        this.bottom = bottom;
    }
}

–> DisplayCutout API官方文档

layoutInDisplayCutoutMode 窗口属性

针对刘海屏(包括瀑布屏),Android 11 提供了4种窗口布局属性 layoutInDisplayCutoutMode,以提供给应用为设备屏幕缺口周围的内容进行布局。应用可以根据需求将此窗口属性设为下列值之一:

窗口属性
LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS

各个属性值说明如下:

(1) LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT

默认情况下,全屏窗口不会使用到刘海区域,非全屏窗口可正常使用刘海区域。仅仅当系统提供的 bar 完全包含了缺口区时才允许 window 延伸到缺口区,否则 window 不会和屏幕缺口区重叠。

//在实际使用中,这意味着如果窗口没有设置以下几种属性:

  • FLAG_FULLSCREEN
  • View.SYSTEM_UI_FLAG_FULLSCREEN
  • View.SYSTEM_UI_FLAG_HIDE_NAVIGATION),

如果缺口在屏幕顶部(或底部),它可以延伸到竖屏的缺口区域。否则(即全屏或横屏)它不会和缺口区域重叠。 也就是说此模式在全屏下不允许使用缺口区域。

(2) LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES

窗口声明使用刘海区域,允许 window 延伸到短边的缺口区。

(3) LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER

窗口声明不使用刘海区域,不允许 window 延伸到缺口区。

(4) LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS

窗口总是允许扩展到屏幕所有边缘的缺口区。

官方相关技术文档

如需详细了解瀑布屏相关官方技术文档,请参阅以下官方链接:

DisplayCutout

WindowInsets

WindowInsets.getDisplayCutout()

layoutInDisplayCutoutMode

android:windowLayoutInDisplayCutoutMode

瀑布屏应用适配

调试

  • 瀑布屏真机调试
  • 模拟器模拟(Android 11以上)

    设置–系统–开发者选项–刘海屏–瀑布刘海屏,可以看到设置完,整个系统页面都进行了更新, 屏幕两端进行了一定边距的留白,由此模拟瀑布屏的左右曲边弧长。

应用适配瀑布屏

瀑布屏手机屏幕边缘有弧形区域,一般来讲,非必要情况下这些区域不应该显示内容或响应应用操作。如果应用程序必须在该区域显示UI或处理那些insets中的触摸输入,需要格外小心,因为弧形区域可能会影响清晰度,并经常导致意外的触摸输入。

要确定这些屏幕缺口区域是否存在及其位置,可以使用 getDisplayCutout()函数获取DisplayCutout实例。利用DisplayCutout提供的相关API对应用进行适配。

如需在瀑布区域中呈现应用内容,可执行以下操作:

  • 调用 DisplayCutout.getWaterfallInsets() 以获取瀑布边衬区的精确尺寸
  • 将窗口布局属性 layoutInDisplayCutoutMode 设为 LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS,以允许窗口延伸到屏幕各个边缘上的刘海和瀑布区域。您必须确保刘海或瀑布区域中没有重要的内容。

注意:如果未将上述窗口布局属性设为 LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS,Android 会在黑边模式下显示窗口,从而避开缺口和瀑布区域。

瀑布屏获取相关参数举例:

DisplayCutout mDisplayCutout = getRootWindowInsets().getDisplayCutout();
Insets mInsets = mDisplayCutout.getWaterfallInsets();
int left = mInsets.left;
int right = mInsets.right;

设置窗口属性举例:

WindowManager.LayoutParams lp = getWindow().getAttributes();  
lp.layoutInDisplayCutoutMode
= WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;  
getWindow().setAttributes(lp);

参考资料

https://developer.android.com/reference/android/view/DisplayCutout

https://developer.android.google.cn/reference/android/view/DisplayCutout.html

https://developer.android.com/preview/features

https://source.android.com/devices/tech/display/display-cutouts

《三星钻孔屏适配指导》

修改说明

作者 版本 修改时间 修改说明
WalkAloner V1.0 2020/11/11 首版

本文地址:https://blog.csdn.net/luzhenrong45/article/details/109631820