支持显示挖孔

尝试 Compose 方法
Jetpack Compose 是 Android 推荐的 UI 工具包。了解如何在 Compose 中使用显示挖孔。

显示挖孔是某些设备上延伸到显示表面的区域。它允许端到端体验,同时为设备正面的重要传感器提供空间。

Android 支持在运行 Android 9(API 级别 28)及更高版本的设备上显示挖孔。但是,设备制造商也可以支持在运行 Android 8.1 或更低版本的设备上显示挖孔。

本文档介绍如何实现对具有挖孔的设备的支持,包括如何使用挖孔区域——即包含挖孔的显示表面上的端到端矩形。

An image showing an example of top-center display cutout
图 1. 1 显示挖孔。

选择您的应用如何处理挖孔区域

如果您不希望您的内容与挖孔区域重叠,通常只需确保您的内容不与状态栏和导航栏重叠即可。如果您正在渲染到挖孔区域,请使用WindowInsetsCompat.getDisplayCutout()检索包含每个挖孔的安全内边距和边界框的DisplayCutout对象。这些API允许您检查您的内容是否与挖孔重叠,以便您可以在需要时重新定位。

您还可以确定内容是否在挖孔区域后面布局。layoutInDisplayCutoutMode窗口布局属性控制您的内容如何在挖孔区域中绘制。您可以将layoutInDisplayCutoutMode设置为以下值之一

  • LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT:当显示挖孔包含在系统栏中时,内容会渲染到挖孔区域。否则,窗口不会与显示挖孔重叠;例如,在横向模式下显示时,内容可能会添加黑边。如果您的应用面向 SDK 35,则对于非浮动窗口,这将解释为ALWAYS
  • LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS:内容始终允许扩展到挖孔区域。如果您的应用面向 SDK 35 并在 Android 15 设备上运行,这是非浮动窗口确保端到端显示的唯一允许模式。
  • LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES:内容在纵向和横向模式下都会渲染到挖孔区域。不要用于浮动窗口。如果您的应用面向 SDK 35,则对于非浮动窗口,这将解释为ALWAYS
  • LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER:内容永远不会渲染到挖孔区域。如果您的应用面向 SDK 35,则对于非浮动窗口,这将解释为ALWAYS

您可以通过编程方式或通过在活动中设置样式来设置挖孔模式。以下示例定义了一个样式,以将LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES属性应用于活动。

<style name="ActivityTheme">
  <item name="android:windowLayoutInDisplayCutoutMode">
    shortEdges <!-- default, shortEdges, or never -->
  </item>
</style>

以下部分将更详细地描述不同的挖孔模式。

默认行为

如果您的应用面向 SDK 35 并在 Android 15 设备上运行,LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS 是默认行为,LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT 将被解释为非浮动窗口的LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS

否则,LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT 是默认值。

在短边挖孔区域中渲染内容

如果您的应用面向 SDK 35 并在 Android 15 设备上运行,LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES 将被解释为非浮动窗口的LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS

使用LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES,内容会在纵向和横向显示的短边上扩展到挖孔区域,而不管系统栏是否隐藏或可见。使用此模式时,请确保没有重要内容与挖孔区域重叠。

下图是设备在纵向模式下LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES的示例

An image showing content rendering into the cutout area while in portrait mode
图 2. 内容在纵向模式下渲染到挖孔区域。

下图是设备在横向模式下LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES的示例

An image showing content rendering into the cutout area while in landscape mode
图 3. 内容在横向模式下渲染到挖孔区域。

在此模式下,窗口在纵向和横向模式下都会扩展到显示短边上的挖孔下方,而不管窗口是否隐藏系统栏。

角落的挖孔被认为是在短边上

An image showing a device with a corner cutout
图 4. 带有角部挖孔的设备。

从不渲染显示挖孔区域的内容

如果您的应用面向 SDK 35 并在 Android 15 设备上运行,LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER 将被解释为非浮动窗口的LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS

使用LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER,窗口永远不允许与挖孔区域重叠。

以下是纵向模式下LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER的示例

An image showing the LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER for portrait
图 5. 纵向模式下LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER的示例。

以下是横向模式下LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER的示例

An image showing the LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER for landscape
图 6. 横向模式下LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER的示例。

显示挖孔支持的最佳实践

在使用显示挖孔时,请考虑以下几点

  • 注意UI关键元素的位置。不要让挖孔区域遮挡任何重要的文本、控件或其他信息。
  • 不要将需要精细触摸识别的任何交互式元素放置或扩展到挖孔区域。挖孔区域的触摸灵敏度可能较低。
  • 尽可能使用WindowInsetsCompat来获取状态栏高度并确定应用于内容的适当填充。避免硬编码状态栏高度,因为这可能导致内容重叠或被截断。

    An image showing content cut at the top for improper insets setup
    图7. 使用WindowInsetsCompat避免内容重叠或被截断。
  • 使用View.getLocationInWindow()来确定你的应用使用了多少窗口空间。不要假设应用使用了整个窗口,并且不要使用View.getLocationOnScreen()

  • 如果你的应用需要在沉浸模式下切换进出,请使用alwaysshortEdgesnever挖孔模式。默认的挖孔行为可能会导致你的应用中的内容在系统栏存在时渲染在挖孔区域中,但在沉浸模式下则不会。这会导致内容在转换过程中上下移动,如下例所示。

    An image showing content moving up and down during transitions.
    图8. 转换过程中内容上下移动的示例。
  • 在沉浸模式下,小心使用窗口坐标与屏幕坐标,因为你的应用在带黑边的屏幕上不会使用整个屏幕。由于黑边,屏幕原点的坐标与窗口原点的坐标不同。你可以根据需要使用getLocationOnScreen()将屏幕坐标转换为视图的坐标。下图显示了内容带黑边时坐标的差异。

    An image showing window versus screen coordinates when content is letterboxed.
    图9. 内容带黑边时窗口坐标与屏幕坐标的对比。
  • 处理MotionEvent时,使用MotionEvent.getX()MotionEvent.getY()来避免类似的坐标问题。不要使用MotionEvent.getRawX()MotionEvent.getRawY()

测试你的内容渲染方式

测试应用的所有屏幕和体验。如果可能,在具有不同类型挖孔的设备上进行测试。如果你没有带挖孔的设备,你可以在运行 Android 9 或更高版本的任何设备或模拟器上模拟常见的挖孔配置,方法如下:

  1. 启用开发者选项
  2. 开发者选项屏幕中,向下滚动到绘图部分,然后选择模拟带挖孔的显示屏
  3. 选择挖孔类型。

    An image showing how to simulate a display cutout in the emulator
    图10. 测试内容渲染方式的开发者选项。

更多资源