在搭载 Android 15 或更高版本的设备上以 SDK 35 或更高版本为目标平台后,您的应用将以无边框显示。窗口通过在系统栏后面绘制内容来占据整个显示屏的宽度和高度。系统栏包括状态栏、标题栏和导航栏。
许多应用都有顶部应用栏。顶部应用栏应延伸到屏幕顶部边缘,并显示在状态栏后面。(可选)当内容滚动时,顶部应用栏可以缩小到状态栏的高度。
许多应用还有底部应用栏或底部导航栏。这些栏也应延伸到屏幕底部边缘,并显示在导航栏后面。否则,应用应在导航栏后面显示滚动内容。
在应用中实现无边框布局时,请注意以下几点:
- 启用无边框显示
- 实现自适应布局,以针对不同规格的设备优化用户体验
- 处理任何视觉重叠
- 考虑在系统栏后面显示遮罩层
启用无边框显示
如果您的应用以 SDK 35 或更高版本为目标平台,则在搭载 Android 15 或更高版本的设备上会自动启用无边框显示。
如需在较低 Android 版本上启用无边框显示,请在 onCreate 的 Activity 中手动调用
enableEdgeToEdge。
Kotlin
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.enableEdgeToEdge(window)
...
}
Java
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
WindowCompat.enableEdgeToEdge(getWindow());
...
}
默认情况下,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 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 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; });
通过对系统栏和刘海屏类型执行逻辑“或”运算,确定 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 组件
许多基于视图的
Android Material 组件
(com.google.android.material) 会自动处理边衬区,包括
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 或更高版本为目标平台,则强制执行无边框显示。
系统状态栏和手势导航栏是透明的,但三按钮导航栏是半透明的。调用 enableEdgeToEdge 以实现向后兼容性。
不过,系统默认设置可能不适用于所有用例。请参阅 Android 系统栏设计指南和无边框设计,以 确定是使用透明系统栏还是半透明系统栏。
创建透明系统栏
以 Android 15 (SDK 35) 或更高版本为目标平台,或者使用默认参数为较低版本调用 enableEdgeToEdge(),以创建透明状态栏。
以 Android 15 或更高版本为目标平台,或者使用默认参数为较低版本调用 enableEdgeToEdge(),以创建透明手势导航栏。对于
三按钮导航栏,请将 Window.setNavigationBarContrastEnforced
设置为 false,否则将应用半透明遮罩层。
创建半透明系统栏
如需创建半透明状态栏,请执行以下操作:
- 将您的
androidx-core依赖项更新为 1.16.0-beta01 或更高版本 - 将 XML 布局封装在
androidx.core.view.insets.ProtectionLayout中,并分配 ID。 - 以编程方式访问
ProtectionLayout以设置保护,指定 状态栏的侧边和GradientProtection。
<androidx.core.view.insets.ProtectionLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/list_protection" android:layout_width="match_parent" android:layout_height="match_parent"> <ScrollView android:id="@+id/item_list" android:clipToPadding="false" android:layout_width="match_parent" android:layout_height="match_parent"> <!--items--> </ScrollView> </androidx.core.view.insets.ProtectionLayout>
findViewById<ProtectionLayout>(R.id.list_protection) .setProtections( listOf( GradientProtection( WindowInsetsCompat.Side.TOP, // Ideally, this is the pane's background color paneBackgroundColor ) ) )
确保传递到 GradientProtection 中的 ColorInt 与内容背景匹配。例如,在可折叠设备上显示的列表详情布局可能具有不同的 GradientProtections,列表面板和详情面板的颜色也不同。
请勿创建半透明手势导航栏。如需创建半透明三按钮导航栏,请执行以下任一操作:
- 如果您已将布局封装在
ProtectionView中,则可以将额外的ColorProtection或GradientProtection传递给setProtections方法。在执行此操作之前,请确保window.isNavigationBarContrastEnforced = false。 - 否则,请设置
window.isNavigationBarContrastEnforced = true。
其他提示
处理边衬区时的其他提示。
实现滚动内容的无边框显示
通过处理边衬区并将 clipToPadding 设置为 false,检查 RecyclerView 或 NestedScrollView 中的最后一个列表项是否被系统栏遮盖。
以下视频展示了停用(左侧)和启用(右侧)无边框显示的 RecyclerView:
如需查看代码示例,请参阅 使用 RecyclerView 创建动态列表 部分中的代码段。
实现全屏对话框的无边框显示
如需实现全屏对话框的无边框显示,请对对话框调用 enableEdgeToEdge。
Kotlin
class MyAlertDialogFragment : DialogFragment() {
override fun onStart(){
super.onStart()
dialog?.window?.let { WindowCompat.enableEdgeToEdge(it) }
}
...
}
Java
public class MyAlertDialogFragment extends DialogFragment {
@Override
public void onStart() {
super.onStart();
Dialog dialog = getDialog();
if (dialog != null) {
Window window = dialog.getWindow();
if (window != null) {
WindowCompat.enableEdgeToEdge(window);
}
}
}
...
}
其他资源
如需详细了解如何实现无边框显示,请参阅以下参考资料。
博客
设计
其他文档
视频