Sau khi bạn nhắm đến SDK 35 trở lên trên thiết bị chạy Android 15 trở lên, ứng dụng của bạn sẽ hiển thị tràn viền. Cửa sổ này trải dài trên toàn bộ chiều rộng và chiều cao của màn hình bằng cách vẽ phía sau thanh hệ thống. Thanh hệ thống bao gồm thanh trạng thái, thanh chú thích và thanh điều hướng.
Nhiều ứng dụng có thanh ứng dụng trên cùng. Thanh ứng dụng trên cùng phải kéo dài đến cạnh trên cùng của màn hình và hiển thị phía sau thanh trạng thái. Nếu muốn, bạn có thể thu nhỏ thanh ứng dụng trên cùng thành chiều cao của thanh trạng thái khi nội dung cuộn.
Nhiều ứng dụng cũng có thanh ứng dụng dưới cùng hoặc thanh điều hướng phía dưới. Các thanh này cũng phải kéo dài đến cạnh dưới của màn hình và hiển thị phía sau thanh điều hướng. Nếu không, ứng dụng sẽ hiển thị nội dung cuộn phía sau thanh điều hướng.
Khi triển khai bố cục tràn viền trong ứng dụng, hãy lưu ý những điều sau:
- Bật chế độ hiển thị tràn viền
- Triển khai bố cục thích ứng để tối ưu hoá trải nghiệm người dùng trên nhiều kiểu dáng thiết bị
- Xử lý mọi trường hợp chồng chéo hình ảnh
- Cân nhắc việc hiển thị lớp phủ phía sau thanh hệ thống
Cho phép hiển thị tràn viền
Nếu ứng dụng của bạn nhắm đến SDK 35 trở lên, thì chế độ tràn viền sẽ tự động được bật cho thiết bị Android 15 trở lên.
Để bật chế độ tràn viền trên các phiên bản Android trước, hãy gọi enableEdgeToEdge theo cách thủ công trong onCreate của Activity.
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());
...
}
Theo mặc định, enableEdgeToEdge() làm cho các thanh hệ thống trở nên trong suốt, ngoại trừ ở chế độ thao tác bằng 3 nút, trong đó thanh điều hướng có một lớp mờ. Màu sắc của các biểu tượng hệ thống và lớp phủ được điều chỉnh dựa trên giao diện sáng hoặc tối của hệ thống.
Để bật chế độ hiển thị tràn viền trong ứng dụng mà không cần dùng hàm enableEdgeToEdge(), hãy xem phần Thiết lập chế độ hiển thị tràn viền theo cách thủ công.
Xử lý các phần chồng chéo bằng cách sử dụng phần lồng ghép
Một số khung hiển thị của ứng dụng có thể vẽ phía sau thanh hệ thống, như trong hình 3.
Bạn có thể giải quyết tình trạng chồng chéo bằng cách phản ứng với các phần lồng ghép, trong đó chỉ định phần nào của màn hình giao nhau với giao diện người dùng hệ thống, chẳng hạn như thanh điều hướng hoặc thanh trạng thái. Giao nhau có thể có nghĩa là hiển thị phía trên nội dung, nhưng cũng có thể thông báo cho ứng dụng của bạn về các cử chỉ hệ thống.
Các loại phần lồng ghép áp dụng cho việc hiển thị ứng dụng tràn viền là:
Phần lồng ghép thanh hệ thống: phù hợp nhất cho các thành phần hiển thị có thể nhấn vào và không được bị thanh hệ thống che khuất.
Phần lồng ghép vết cắt trên màn hình: cho những khu vực có thể có vết cắt trên màn hình do hình dạng của thiết bị.
Phần lồng ghép cử chỉ hệ thống: cho các vùng điều hướng bằng cử chỉ mà hệ thống sử dụng và được ưu tiên hơn ứng dụng của bạn.
Lồng ghép thanh hệ thống
Khoảng lùi thanh hệ thống là loại khoảng lùi thường dùng nhất. Các phần lồng ghép này đại diện cho khu vực mà giao diện người dùng hệ thống hiển thị trong trục Z phía trên ứng dụng của bạn. Bạn nên sử dụng các phần lồng ghép này để di chuyển hoặc đệm các khung hiển thị trong ứng dụng có thể nhấn vào và không được bị che khuất bởi các thanh hệ thống.
Ví dụ: nút hành động nổi (FAB) trong hình 3 bị thanh điều hướng che khuất một phần:
Để tránh loại chồng chéo hình ảnh này ở chế độ cử chỉ hoặc chế độ nút, bạn có thể tăng lề của khung hiển thị bằng cách sử dụng getInsets(int) với WindowInsetsCompat.Type.systemBars().
Ví dụ về mã sau đây cho thấy cách triển khai phần lồng ghép thanh hệ thống:
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; });
Nếu bạn áp dụng giải pháp này cho ví dụ trong hình 3, thì kết quả là không có sự chồng chéo hình ảnh ở chế độ nút, như trong hình 4:
Điều tương tự cũng áp dụng cho chế độ điều hướng bằng cử chỉ, như minh hoạ trong hình 5:
Vết cắt trên màn hình
Một số thiết bị có vết cắt trên màn hình. Thông thường, phần cắt nằm ở đầu màn hình và nằm trong thanh trạng thái. Khi màn hình thiết bị ở chế độ ngang, phần cắt có thể nằm ở cạnh dọc. Tuỳ thuộc vào nội dung mà ứng dụng của bạn hiển thị trên màn hình, bạn nên triển khai khoảng đệm để tránh vết cắt trên màn hình, vì theo mặc định, ứng dụng sẽ vẽ trong vết cắt trên màn hình.
Ví dụ: nhiều màn hình ứng dụng hiển thị danh sách các mục. Đừng che khuất các mục trong danh sách bằng vết cắt trên màn hình hoặc thanh hệ thống.
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; });
Xác định giá trị của WindowInsetsCompat bằng cách lấy or logic của các thanh hệ thống và các loại vết cắt trên màn hình.
Đặt clipToPadding thành RecyclerView để khoảng đệm cuộn cùng với các mục trong danh sách. Điều này cho phép các mục nằm phía sau thanh hệ thống khi người dùng cuộn, như trong ví dụ sau.
<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" />
Phần lồng ghép cử chỉ hệ thống
Phần lồng ghép cử chỉ hệ thống thể hiện các khu vực của cửa sổ nơi cử chỉ hệ thống được ưu tiên hơn ứng dụng của bạn. Các khu vực này được thể hiện bằng màu cam trong hình 6:
Tương tự như phần lồng ghép thanh hệ thống, bạn có thể tránh chồng chéo phần lồng ghép cử chỉ hệ thống bằng cách sử dụng getInsets(int) với WindowInsetsCompat.Type.systemGestures().
Sử dụng các phần lồng ghép này để di chuyển hoặc đệm các thành phần hiển thị có thể vuốt ra khỏi các cạnh. Các trường hợp sử dụng phổ biến bao gồm bảng dưới cùng, thao tác vuốt trong trò chơi và băng chuyền được triển khai bằng ViewPager2.
Trên Android 10 trở lên, phần lồng ghép cử chỉ hệ thống chứa một phần lồng ghép dưới cùng cho cử chỉ màn hình chính, cũng như một phần lồng ghép bên trái và bên phải cho cử chỉ quay lại:
Ví dụ về mã sau đây cho thấy cách triển khai phần lồng ghép cử chỉ hệ thống:
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; });
Thành phần Material
Nhiều Thành phần Material cho Android dựa trên khung hiển thị (com.google.android.material) tự động xử lý các phần lồng ghép, bao gồm BottomAppBar, BottomNavigationView, NavigationRailView và NavigationView
Tuy nhiên, AppBarLayout không tự động xử lý các phần lồng ghép. Thêm android:fitsSystemWindows="true" để xử lý các phần lồng ghép trên cùng.
Đọc cách xử lý phần lồng ghép bằng Thành phần Material trong Compose.
Gửi phần lồng ghép tương thích ngược
Để ngăn các phần lồng ghép gửi đến khung hiển thị con và tránh khoảng đệm quá mức, bạn có thể sử dụng các phần lồng ghép bằng hằng số WindowInsetsCompat.CONSUMED. Tuy nhiên, trên các thiết bị chạy Android 10 (cấp độ API 29 trở xuống), các khoảng lùi không được gửi đến các thành phần đồng cấp sau khi gọi WindowInsetsCompat.CONSUMED, điều này có thể gây ra sự chồng chéo hình ảnh ngoài ý muốn.
Để xác nhận rằng các phần lồng ghép được gửi đến các thành phần đồng cấp cho tất cả phiên bản Android được hỗ trợ, hãy sử dụng ViewGroupCompat#installCompatInsetsDispatch trước khi sử dụng các phần lồng ghép, có trên AndroidX Core và Core-ktx 1.16.0-alpha01 trở lên.
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);
Chế độ hiển thị tối đa
Một số nội dung nên được trải nghiệm ở chế độ toàn màn hình để mang lại cho người dùng trải nghiệm sống động hơn. Bạn có thể ẩn thanh hệ thống cho chế độ hiển thị tối đa bằng cách sử dụng thư viện WindowInsetsController và 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());
Tham khảo bài viết Ẩn thanh hệ thống cho chế độ hiển thị tối đa để biết thêm thông tin về cách triển khai tính năng này.
Biểu tượng thanh hệ thống
Việc gọi enableEdgeToEdge đảm bảo màu sắc biểu tượng thanh hệ thống sẽ cập nhật khi giao diện thiết bị thay đổi.
Khi chuyển sang chế độ tràn viền, bạn có thể cần cập nhật màu biểu tượng thanh hệ thống theo cách thủ công để các biểu tượng này tương phản với nền của ứng dụng. Ví dụ: để tạo biểu tượng thanh trạng thái sáng:
Kotlin
WindowCompat.getInsetsController(window, window.decorView) .isAppearanceLightStatusBars = false
Java
WindowCompat.getInsetsController(window, window.getDecorView()) .setAppearanceLightStatusBars(false);
Bảo vệ thanh hệ thống
Sau khi ứng dụng của bạn nhắm đến SDK 35 trở lên, chế độ tràn viền sẽ được thực thi.
Thanh trạng thái hệ thống và thanh điều hướng bằng cử chỉ có màu trong suốt, nhưng thanh điều hướng bằng 3 nút có màu trong mờ. Gọi enableEdgeToEdge để đảm bảo khả năng tương thích ngược.
Tuy nhiên, chế độ mặc định của hệ thống có thể không phù hợp cho một số trường hợp sử dụng. Tham khảo hướng dẫn thiết kế thanh hệ thống Android và Thiết kế tràn viền để xác định xem nên sử dụng thanh hệ thống trong suốt hay mờ.
Tạo thanh hệ thống trong suốt
Tạo thanh trạng thái trong suốt bằng cách nhắm đến Android 15 (SDK 35) trở lên hoặc bằng cách gọi enableEdgeToEdge() với các đối số mặc định cho các phiên bản cũ.
Tạo thanh điều hướng bằng cử chỉ trong suốt bằng cách nhắm đến Android 15 trở lên hoặc
bằng cách gọi enableEdgeToEdge() với các đối số mặc định cho các phiên bản cũ hơn. Đối với thanh điều hướng bằng 3 nút, hãy đặt Window.setNavigationBarContrastEnforced thành false, nếu không, một lớp phủ mờ sẽ được áp dụng.
Tạo thanh hệ thống mờ
Để tạo thanh trạng thái mờ, hãy làm như sau:
- Cập nhật phần phụ thuộc
androidx-corelên phiên bản 1.16.0-beta01 trở lên - Gói bố cục XML của bạn trong
androidx.core.view.insets.ProtectionLayoutvà gán một mã nhận dạng. - Truy cập theo phương thức lập trình vào
ProtectionLayoutđể đặt biện pháp bảo vệ, chỉ định bên vàGradientProtectioncho thanh trạng thái.
<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 ) ) )
Đảm bảo ColorInt được truyền vào GradientProtection khớp với nền nội dung. Ví dụ: bố cục danh sách-chi tiết hiển thị trên thiết bị có thể gập lại có thể có GradientProtections khác nhau với màu sắc khác nhau cho bảng điều khiển danh sách và bảng điều khiển chi tiết.
Đừng tạo thanh điều hướng bằng cử chỉ có màu mờ. Để tạo thanh điều hướng ba nút mờ, hãy làm theo một trong những cách sau:
- Nếu đã gói bố cục trong
ProtectionView, bạn có thể truyền thêmColorProtectionhoặcGradientProtectionvào phương thứcsetProtections. Trước khi làm việc này, hãy đảm bảowindow.isNavigationBarContrastEnforced = false. - Nếu không, hãy đặt
window.isNavigationBarContrastEnforced = true.
Các mẹo khác
Các mẹo khác khi xử lý phần lồng ghép.
Làm cho nội dung cuộn tràn viền
Kiểm tra để đảm bảo mục cuối cùng trong danh sách không bị các thanh hệ thống che khuất trong RecyclerView hoặc NestedScrollView bằng cách xử lý các phần lồng ghép và đặt clipToPadding thành false.
Video sau đây cho thấy một RecyclerView khi tắt chế độ hiển thị tràn viền (bên trái) và khi bật chế độ này (bên phải):
Hãy xem các đoạn mã trong phần Tạo danh sách động bằng RecyclerView để biết mã mẫu.
Đặt hộp thoại toàn màn hình ở chế độ tràn viền
Để đặt hộp thoại toàn màn hình thành tràn viền, hãy gọi enableEdgeToEdge trên Dialog.
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);
}
}
}
...
}
Tài nguyên khác
Hãy xem các tài liệu tham khảo sau để biết thêm thông tin về cách chuyển sang chế độ tràn viền.
Blog
- Mẹo xử lý phần lồng ghép cho biện pháp thực thi chế độ tràn viền của Android 15
- WindowInsets — trình nghe bố cục
Thiết kế
Tài liệu khác
Video