在搭载 Android 15 或更高版本的设备上以 SDK 35 或更高版本为目标平台后,您的应用将以边到边的方式显示。窗口会在系统栏后面绘制,从而跨越整个显示屏的宽度和高度。系统栏包括状态栏、标题栏和导航栏。
许多应用都有顶部应用栏。顶部应用栏应延伸到屏幕顶部,并显示在状态栏后面。可选地,在内容滚动时,顶部应用栏可以缩小到状态栏的高度。
许多应用还具有底部应用栏或底部导航栏。这些栏也应延伸到屏幕底部边缘,并显示在导航栏后面。否则,应用应在导航栏后面显示滚动内容。
在应用中实现边到边布局时,请注意以下几点:
- 启用无边框显示
- 处理任何视觉重叠问题。
- 考虑在系统栏后面显示遮罩。
启用无边框显示
如果您的应用以 SDK 35 或更高版本为目标平台,系统会自动为搭载 Android 15 或更高版本的设备启用无边框设计。
如需在旧版 Android 上启用边到边功能,请执行以下操作:
在应用或模块的
build.gradle
文件中,向androidx.activity
库添加依赖项:Kotlin
dependencies { val activity_version =
activity_version
// Java language implementation implementation("androidx.activity:activity:$activity_version") // Kotlin implementation("androidx.activity:activity-ktx:$activity_version") }Groovy
dependencies { def activity_version =
activity_version
// Java language implementation implementation 'androidx.activity:activity:$activity_version' // Kotlin implementation 'androidx.activity:activity-ktx:$activity_version' }将
enableEdgeToEdge
扩展函数导入您的应用:
在 Activity
的 onCreate
中调用 enableEdgeToEdge
以手动启用无边框。应在 setContentView
之前调用此方法。
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { enableEdgeToEdge() super.onCreate(savedInstanceState) ... }
Java
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { EdgeToEdge.enable(this); super.onCreate(savedInstanceState); ... }
默认情况下,enableEdgeToEdge()
会使系统栏透明,但在三按钮导航模式下,状态栏会获得半透明的遮罩。系统图标和遮罩的颜色会根据系统的浅色或深色主题进行调整。
enableEdgeToEdge()
函数会自动声明应用应采用边到边布局,并调整系统栏的颜色。
如需在不使用 enableEdgeToEdge()
函数的情况下在应用中启用无边框显示,请参阅手动设置无边框显示。
使用边衬区处理重叠
应用的某些视图可能会在系统栏后面绘制,如图 3 所示。
您可以通过响应内边距来解决重叠问题,内边距用于指定屏幕的哪些部分与系统界面(例如导航栏或状态栏)相交。交叉可能意味着在内容上方显示,但也可以告知应用系统手势。
适用于全屏显示应用的内边距类型包括:
系统栏边衬区:最适合可点按且不得被系统栏遮挡的视图。
显示屏刘海:适用于可能因设备形状而存在刘海屏的区域。
系统手势内边距:用于系统使用的手势导航区域,优先级高于您的应用。
系统栏内嵌
系统栏内边距是最常用的内边距类型。它们表示系统界面在应用上方 Z 轴中显示的区域。它们最适合用于移动或填充应用中可点按且不得被系统栏遮挡的视图。
例如,图 3 中的悬浮操作按钮 (FAB) 被导航栏部分遮挡:
如需避免在手势模式或按钮模式下出现此类视觉重叠,您可以将 getInsets(int)
与 WindowInsetsCompat.Type.systemBars()
结合使用,以增加视图的外边距。
以下代码示例展示了如何实现系统栏内边距:
Kotlin
ViewCompat.setOnApplyWindowInsetsListener(fab) { v, windowInsets -> val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) // Apply the insets as a margin to the view. This solution sets // only the bottom, left, and right dimensions, but you can apply whichever // insets are appropriate to your layout. You can also update the view padding // if that's more appropriate. v.updateLayoutParams<MarginLayoutParams> { leftMargin = insets.left bottomMargin = insets.bottom rightMargin = insets.right } // Return CONSUMED if you don't want want the window insets to keep passing // down to descendant views. WindowInsetsCompat.CONSUMED }
Java
ViewCompat.setOnApplyWindowInsetsListener(fab, (v, windowInsets) -> { Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()); // Apply the insets as a margin to the view. This solution sets only the // bottom, left, and right dimensions, but you can apply whichever insets are // appropriate to your layout. You can also update the view padding if that's // more appropriate. MarginLayoutParams mlp = (MarginLayoutParams) v.getLayoutParams(); mlp.leftMargin = insets.left; mlp.bottomMargin = insets.bottom; mlp.rightMargin = insets.right; v.setLayoutParams(mlp); // Return CONSUMED if you don't want want the window insets to keep passing // down to descendant views. return WindowInsetsCompat.CONSUMED; });
如果您将此解决方案应用于图 3 中显示的示例,则会导致按钮模式下没有视觉重叠,如图 4 所示:
同样,手势导航模式也是如此,如图 5 所示:
刘海屏内嵌
某些设备具有显示屏缺口。通常,刘海屏位于屏幕顶部,并包含在状态栏中。当设备屏幕处于横屏模式时,缺口可能位于垂直边缘。根据应用在屏幕上显示的内容,您应实现内边距以避免出现刘海屏,因为默认情况下,应用会在刘海屏中绘制。
例如,许多应用屏幕都会显示项列表。请勿使用显示屏缺口或系统栏遮挡列表项。
Kotlin
ViewCompat.setOnApplyWindowInsetsListener(binding.recyclerView) { v, insets -> val bars = insets.getInsets( WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() ) v.updatePadding( left = bars.left, top = bars.top, right = bars.right, bottom = bars.bottom, ) WindowInsetsCompat.CONSUMED }
Java
ViewCompat.setOnApplyWindowInsetsListener(mBinding.recyclerView, (v, insets) -> { Insets bars = insets.getInsets( WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout() ); v.setPadding(bars.left, bars.top, bars.right, bars.bottom); return WindowInsetsCompat.CONSUMED; });
通过对系统栏和显示屏切口类型进行逻辑 or 运算来确定 WindowInsetsCompat
的值。
将 clipToPadding
设置为 RecyclerView
,以便内边距随列表项滚动。这样,当用户滚动时,项就会位于系统栏后面,如以下示例所示。
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
系统手势内边距
系统手势内嵌表示窗口中系统手势的优先级高于应用的手势的区域。图 6 中,这些区域用橙色标记:
与系统栏内边距一样,您可以将 getInsets(int)
与 WindowInsetsCompat.Type.systemGestures()
搭配使用,以避免与系统手势内边距重叠。
使用这些边衬区将可滑动视图移离边缘或为其添加内边距。常见用例包括底部动作条、游戏中的滑动操作,以及使用 ViewPager2
实现的轮播界面。
在 Android 10 或更高版本中,系统手势内边距包含用于主屏幕手势的底部内边距,以及用于返回手势的左侧和右侧内边距:
以下代码示例展示了如何实现系统手势内边距:
Kotlin
ViewCompat.setOnApplyWindowInsetsListener(view) { view, windowInsets -> val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures()) // Apply the insets as padding to the view. Here, set all the dimensions // as appropriate to your layout. You can also update the view's margin if // more appropriate. view.updatePadding(insets.left, insets.top, insets.right, insets.bottom) // Return CONSUMED if you don't want the window insets to keep passing down // to descendant views. WindowInsetsCompat.CONSUMED }
Java
ViewCompat.setOnApplyWindowInsetsListener(view, (v, windowInsets) -> { Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures()); // Apply the insets as padding to the view. Here, set all the dimensions // as appropriate to your layout. You can also update the view's margin if // more appropriate. view.setPadding(insets.left, insets.top, insets.right, insets.bottom); // Return CONSUMED if you don't want the window insets to keep passing down // to descendant views. return WindowInsetsCompat.CONSUMED; });
Material 组件
许多基于 View 的 Android Material 组件 (com.google.android.material){:.external} 会自动处理边衬区,包括 BottomAppBar
、BottomNavigationView
、NavigationRailView
和 NavigationView
不过,AppBarLayout
不会自动处理内边距。添加 android:fitsSystemWindows="true"
以处理顶部边衬区。
了解如何使用 Compose 中的 Material 组件处理内边距。
向后兼容的内边距调度
如需停止将内边距分派给子视图并避免过度内边距,您可以使用 WindowInsetsCompat.CONSUMED
常量使用内边距。不过,在搭载 Android 10(API 级别 29 及更低版本)的设备上,调用 WindowInsetsCompat.CONSUMED
后,系统不会将内边距分派给同级兄弟元素,这可能会导致意外的视觉重叠。
如需确认是否已将内边距分派给所有受支持的 Android 版本的兄弟元素,请在使用内边距之前使用 ViewGroupCompat#installCompatInsetsDispatch
(可在 AndroidX Core 和 Core-ktx 1.16.0-alpha01 及更高版本中使用)。
Kotlin
// Use the i.d. assigned to your layout's root view, e.g. R.id.main val rootView = findViewById(R.id.main) // Call before consuming insets ViewGroupCompat.installCompatInsetsDispatch(rootView)
Java
// Use the i.d. assigned to your layout's root view, e.g. R.id.main LinearLayout rootView = findViewById(R.id.main); // Call before consuming insets ViewGroupCompat.installCompatInsetsDispatch(rootView);
沉浸模式
有些内容在全屏模式下会让用户获得最佳体验,从而获得更身临其境的体验。您可以使用 WindowInsetsController
和 WindowInsetsControllerCompat
库隐藏沉浸模式下的系统栏:
Kotlin
val windowInsetsController = WindowCompat.getInsetsController(window, window.decorView) // Hide the system bars. windowInsetsController.hide(Type.systemBars()) // Show the system bars. windowInsetsController.show(Type.systemBars())
Java
Window window = getWindow(); WindowInsetsControllerCompat windowInsetsController = WindowCompat.getInsetsController(window, window.getDecorView()); if (windowInsetsController == null) { return; } // Hide the system bars. windowInsetsController.hide(WindowInsetsCompat.Type.systemBars()); // Show the system bars. windowInsetsController.show(WindowInsetsCompat.Type.systemBars());
如需详细了解如何实现此功能,请参阅隐藏系统栏以实现沉浸模式。
系统栏图标
调用 enableEdgeToEdge
可确保在设备主题发生变化时系统栏图标颜色更新。
在采用无边框设计时,您可能需要手动更新系统栏图标颜色,使其与应用的背景形成对比。例如,如需创建浅色状态栏图标,请执行以下操作:
Kotlin
WindowCompat.getInsetsController(window, window.decorView) .isAppearanceLightStatusBars = false
Java
WindowCompat.getInsetsController(window, window.getDecorView()) .setAppearanceLightStatusBars(false);
系统栏保护
当您的应用以 SDK 35 或更高版本为目标平台后,系统会强制采用无边框设计。系统状态栏和手势导航栏是透明的,但三按钮导航栏是半透明的。
如需移除默认的半透明三按钮导航栏背景保护,请将 Window.setNavigationBarContrastEnforced
设置为 false
。
其他资源
如需详细了解 WindowInsets
、手势导航和边衬区的工作原理,请参阅以下参考文档: