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 được 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 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 trạng thái thanh phụ đề, 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à phía sau thanh trạng thái. Thanh ứng dụng trên cùng có thể tuỳ ý thu nhỏ theo 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 nên đồng thờ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 thanh. 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 của bạn, hãy lưu giữ những nội dung sau trong tâm trí:

  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 đằng 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 Thiết bị chạy 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 enableEdgeToEdge vào ứng dụng của bạn:

Bật tính năng hiển thị tràn viền theo cách thủ công bằng cách gọi enableEdgeToEdge trong onCreate/Activity của bạn. 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 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 trạng thái có một màn hình trong suốt. Chiến lược phát hành đĩa đơn màu của các biểu tượng hệ thống và màn hình được điều chỉnh theo hệ thống giao diện sáng hoặc tối.

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

Để bật chế độ hiển thị tràn viền trong ứng dụng mà không cần sử dụng Hàm enableEdgeToEdge(), xem 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 phần chồng chéo bằng cách thể hiện các phần lồng ghép, tức là 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 trạng thái thanh. 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ể cung cấp thông tin ứng dụng của bạn về 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 cho các chế độ xem có thể nhấn và không được bị các thanh hệ thống che khuất về mặt thị giác.

  • Các 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: dành 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 nơi giao diện người dùng hệ thống hiển thị trong trục Z phía trên ứng dụng. Chúng tốt nhất dùng để di chuyển hoặc đệm các khung hiển thị trong ứng dụng của bạn mà bạn có thể nhấn vào và không được bị các thanh hệ thống che khuất trực quan.

Ví dụ: thao tác nổi nút (FAB) trong hình 3 là một phần bị thanh điều hướng che khuất:

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 trong bố cục tràn viền.

Để tránh loại hình ảnh trùng lặp này ở chế độ cử chỉ hoặc chế độ nút, bạn có thể tăng lề của chế độ xem bằng cách sử dụng getInsets(int) thông qua tính năng 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, kết quả sẽ không chồng chéo hình ảnh ở 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 vấn đề chồng chéo hình ảnh trong 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 vấn đề chồng chéo hình ảnh trong cử chỉ chế độ điều hướng.

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 và được đưa vào thanh trạng thái. Khi màn hình thiết bị ở chế độ ngang thì vết cắt có thể nằm trên cạnh dọc. Tuỳ thuộc vào nội dung mà ứng dụng của bạn hiện 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, các ứng dụng sẽ vẽ ở 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. Không che khuất các mục trong danh sách với 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 giá trị logic or của 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 mục trong danh sách. Điều này cho phép các mục bỏ qua thanh hệ thống khi người dùng 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 cửa sổ nơi các cử chỉ hệ thống ưu tiên hơn ứng dụng của bạn. Những vùng 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 đang sử dụng getInsets(int) thông qua tính năng 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. Trường hợp sử dụng phổ biến các trường hợp bao gồm bảng dưới cùng, vuốt trong trò chơi và băng chuyền được triển khai bằng cách sử dụ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à 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;
});

Thành phần Material

Thành phần Android Material dựa trên nhiều khung hiển thị (com.google.android.material){:.external} tự động xử lý các phần lồng ghép, bao gồm 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 hoặc sử dụng setOnApplyWindowInsetsListener.

Đọc cách xử lý các phần lồng ghép bằng Thành phần Material trong Compose.

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

Trải nghiệm tốt nhất một số nội dung ở chế độ toàn màn hình, giúp người dùng một trải nghiệm sống động. Bạn có thể ẩn thanh hệ thống cho chế độ hiển thị tối đa bằng cách sử dụng WindowInsetsControllerWindowInsetsControllerCompat thư viện:

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, cử chỉ điều hướng và cách phần lồng ghép hoạt động: