Hiển thị nội dung tràn viền trong ứng dụng của bạn

Thử cách dùng Compose
Jetpack Compose là bộ công cụ giao diện người dùng được đề xuất cho Android. Tìm hiểu cách sử dụng chế độ tràn viền trong Compose.

Sau khi bạn nhắm đến SDK 35 trở lên trên một 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ổ trải dài 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 các 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. Bạn có thể thu nhỏ thanh ứng dụng trên cùng xuống 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 dưới cùng. Các thanh này cũng phải kéo dài đến cạnh dưới cùng 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.

Hình 1. Thanh hệ thống theo bố cục tràn viền.

Khi triển khai bố cục tràn viền trong ứng dụng, hãy lưu ý những điều sau:

  1. Bật màn hình tràn viền
  2. Xử lý mọi hình ảnh trùng lặp.
  3. Cân nhắc hiển thị màn hình chờ phía sau các thanh hệ thống.
ví dụ về hình ảnh phía sau thanh trạng thái
Hình 2. Ví dụ về hình ảnh phía sau thanh trạng thái.

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, chế độ tràn viền sẽ tự động bật cho các thiết bị chạy 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 làm như sau:

  1. Thêm phần phụ thuộc vào thư viện androidx.activity trong tệp build.gradle của ứng dụng hoặc mô-đun:

    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'
    }
  2. Nhập hàm mở rộng enableEdgeToEdge vào ứng dụng:

Bật chế độ tràn viền theo cách thủ công bằng cách gọi enableEdgeToEdge trong onCreate của Activity. Bạn nên gọi phương thức này trước 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);
       ...
     }
   

Theo mặc định, enableEdgeToEdge() làm cho các thanh hệ thống trong suốt, ngoại trừ chế độ điều hướng bằng 3 nút, trong đó thanh trạng thái có một màn chắn trong suốt. Màu sắc của các biểu tượng hệ thống và màn chắn được điều chỉnh dựa trên giao diện sáng hoặc tối của hệ thống.

Hàm enableEdgeToEdge() tự động khai báo rằng ứng dụng phải được bố trí cạnh nhau và điều chỉnh màu sắc của các thanh hệ thống.

Để bật màn hình tràn viền trong ứng dụng mà không cần sử dụng hàm enableEdgeToEdge(), hãy xem phần Thiết lập màn hình tràn viền theo cách thủ công.

Xử lý các phần chồng chéo bằng phần lồng ghép

Một số thành phần hiển thị của ứng dụng có thể vẽ phía sau các thanh hệ thống, như trong hình 3.

Bạn có thể giải quyết vấn đề chồng chéo bằng cách phản ứng với các phần lồng ghép, trong đó chỉ định những 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 bao gồm:

  • Phần lồng ghép thanh hệ thống: phù hợp nhất với các thành phần hiển thị có thể nhấn và không được bị các thanh hệ thống che khuất.

  • Vùng lồng ghép phần cắt trên màn hình: dành cho các khu vực có thể có phần 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ỉ của hệ thống: dành cho các khu vực điều hướng bằng cử chỉ mà hệ thống sử dụng và có mức độ ưu tiên cao hơn ứng dụng của bạn.

Lồng ghép thanh hệ thống

Phần lồng ghép thanh hệ thống là loại phần lồng ghép được sử dụng phổ biến nhất. Các vùng này đại diện cho khu vực mà giao diện người dùng hệ thống hiển thị theo trục Z phía trên ứng dụng. Bạn nên sử dụng các vùng này để di chuyển hoặc đệm các thành phần hiển thị có thể nhấn trong ứng dụng và không được bị các thanh hệ thống che khuất.

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:

ví dụ về cách triển khai tràn viền, nhưng thanh điều hướng đang che khuất FAB
Hình 3. Thanh điều hướng chồng lên một FAB trong bố cục tràn viền.

Để tránh tình trạng 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 thành phần hiển thị bằng cách sử dụng getInsets(int) với WindowInsetsCompat.Type.systemBars().

Mã ví dụ 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 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;
});

Nếu bạn áp dụng giải pháp này cho ví dụ trong hình 3, thì sẽ không có hình ảnh chồng chéo trong chế độ nút, như minh hoạ trong hình 4:

thanh điều hướng mờ không che khuất FAB
Hình 4. Khắc phục tình trạng chồng chéo hình ảnh ở chế độ nút.

Đ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:

tràn viền với tính năng điều hướng bằng cử chỉ
Hình 5. Khắc phục tình trạng chồng chéo hình ảnh ở chế độ điều hướng bằng cử chỉ.

Hiển thị phần lồng ghép vết cắt

Một số thiết bị có phần cắt 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 hiển thị trên màn hình, bạn nên triển khai khoảng đệm để tránh các phần cắt trên màn hình, vì theo mặc định, các ứng dụng sẽ vẽ trong phần 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. Không che khuất các mục trong danh sách bằng phần cắt 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 hoặc logic của các thanh hệ thống và các loại phần cắt 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 các 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 đại diện cho các khu vực của cửa sổ mà 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:

Ví dụ về phần lồng ghép cử chỉ hệ thống
Hình 6. Phần lồng ghép cử chỉ hệ thống.

Giống như các phần lồng ghép thanh hệ thống, bạn có thể tránh chồng chéo các 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 trang 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 thao tác hệ thống chứa phần lồng ghép dưới cùng cho thao tác về màn hình chính, và phần lồng ghép bên trái và bên phải cho thao tác quay lại:

ví dụ về phép đo lồng ghép cử chỉ hệ thống
Hình 7. Đo lường phần lồng ghép cử chỉ hệ thống.

Mã ví dụ 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 của Android dựa trên khung hiển thị (com.google.android.material){:.external} tự động xử lý phần lồng ghép, bao gồm cả BottomAppBar, BottomNavigationView, NavigationRailViewNavigationView

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.

Chế độ hiển thị tối đa

Một số nội dung nên được xem ở chế độ toàn màn hình để mang đến 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 WindowInsetsControllerWindowInsetsControllerCompat:

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());

Hãy tham khảo phần Ẩn thanh hệ thống cho chế độ chìm để biết thêm thông tin về cách triển khai tính năng này.

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ờ.

Để xoá tính năng bảo vệ trong nền của chế độ thao tác bằng 3 nút có màu trong mờ theo mặc định, hãy đặt Window.setNavigationBarContrastEnforced thành false.

Tài nguyên khác

Hãy xem các tài liệu tham khảo sau đây để biết thêm thông tin về WindowInsets, thao tác bằng cử chỉ và cách hoạt động của phần lồng ghép: