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

Thử cách 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 làm việc với tính năng hiển thị tràn viền trong Compose.

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ổ trải rộng 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 phụ đề 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ủa màn hình và hiển thị phía sau thanh trạng thái. Thanh ứng dụng trên cùng có thể thu nhỏ theo chiều cao của thanh trạng thái khi nội dung cuộn (không bắt buộc).

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 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 trường hợp trùng lặp hình ảnh.
  3. Bạn nên chiếu các màn hình phía sau 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.

Bật màn hình tràn viền

Nếu ứng dụng của bạn nhắm đến SDK 35 trở lên, thì chế độ hiển thị tràn viền sẽ tự động được bật cho các thiết bị Android 15 trở lên.

Để bật chế độ hiển thị 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 của bạn:

Bật chế độ hiển thị tràn viền theo cách thủ công bằng cách gọi enableEdgeToEdge trong onCreate của Activity. Hàm này phải được gọi 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() sẽ làm cho thanh hệ thống trong suốt, ngoại trừ chế độ thao tác bằng 3 nút, trong đó thanh trạng thái sẽ có một màn hình trong suốt. Màu của các biểu tượng hệ thống và màn hình đượ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 sẽ được bố trí tràn viền và điều chỉnh màu của các thanh hệ thống.

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

Xử lý trùng lặp 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ư minh hoạ trong hình 3.

Bạn có thể giải quyết các trường hợp trùng lặp bằng cách phản ứng với các phần lồng ghép. Các phần lồng ghép này chỉ định phần nào của màn hình giao 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 điểm có thể đồng nghĩa với việc 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 để hiện ứng dụng tràn viền là:

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

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

Phầ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 thường dùng nhất. Chúng đại diện cho khu vực hiển thị giao diện người dùng hệ thống trên trục Z phía trên ứng dụng của bạn. Tốt nhất là bạn nên dùng chúng để di chuyển hoặc đệm các khung hiển thị trong ứng dụng có thể nhấn được và không 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ề triển khai tràn viền, nhưng thanh điều hướng đang che FAB
Hình 3. Thanh điều hướng chồng lên nút hành động nổi theo 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 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 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ì hình ảnh sẽ không bị chồng chéo ở chế độ nút, như trong hình 4:

một thanh điều hướng trong suốt không che FAB
Hình 4. Giải quyết trường hợp trùng lặp hình ảnh ở chế độ nút.

Điều này cũng áp dụng cho chế độ thao tác bằng cử chỉ, như trong hình 5:

tràn viền với thao tác bằng cử chỉ
Hình 5. Giải quyết trường hợp trùng lặp hình ảnh trong chế độ thao tác bằng cử chỉ.

Phần lồng ghép 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, vết cắt nằm ở đầu màn hình và được đưa vào thanh trạng thái. Khi màn hình thiết bị ở chế độ ngang, vết 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 các vết cắt trên màn hình, vì theo mặc định, các ứng dụng sẽ vẽ vào vết cắt trên màn hình.

Ví dụ: nhiều màn hình ứng dụng cho thấy một 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) -> {
  WindowInsetsCompat 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 logic or 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 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

Các phần lồng ghép cử chỉ hệ thống thể hiện các khu vực trong cửa sổ nơi các cử chỉ hệ thống được ưu tiên hơn ứng dụng của bạn. Những 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 thêm các khung hiển thị có thể vuốt ra xa 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, các 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 và một phần lồng ghép bên trái và bên phải cho các cử chỉ quay lại:

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

Ví dụ về mã sau đây cho thấy cách triển khai các 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;
});

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

Tốt nhất là người dùng sẽ có trải nghiệm tốt nhất khi xem một số nội dung ở chế độ toàn màn hình, nhờ đó có 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());

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.

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 các phần lồng ghép: