Xử lý biện pháp thực thi chế độ tràn viền trên Android 15

1. Trước khi bắt đầu

SociaLite minh hoạ cách dùng nhiều API của Nền tảng Android để triển khai các tính năng thường gặp trong ứng dụng mạng xã hội, tận dụng nhiều API Jetpack để đạt được các chức năng phức tạp nhưng hoạt động ổn định trên nhiều thiết bị và cần ít mã hơn.

Lớp học lập trình này sẽ hướng dẫn bạn về quy trình thiết lập ứng dụng SociaLite tương thích với biện pháp thực thi chế độ tràn viền trên Android 15 và chỉnh sửa để ứng dụng hiển thị tràn viền theo cách tương thích ngược. Sau khi chuyển sang chế độ tràn viền, tuỳ thuộc vào thiết bị và chế độ thao tác của bạn, SociaLite sẽ có giao diện như sau:

Ứng dụng SociaLite ở chế độ thao tác bằng 3 nút.

Ứng dụng SociaLite ở chế độ thao tác bằng cử chỉ.

Ứng dụng SociaLite có chế độ thao tác bằng 3 nút

Ứng dụng SociaLite có chế độ thao tác bằng cử chỉ

Ứng dụng SociaLite trên một thiết bị có màn hình lớn.

Ứng dụng SociaLite trên một thiết bị có màn hình lớn

Điều kiện tiên quyết

  • Kiến thức cơ bản về Kotlin.
  • Hoàn thành lớp học lập trình Thiết lập Android Studio hoặc thành thạo cách dùng Android Studio và cách kiểm thử ứng dụng trong một trình mô phỏng hoặc thiết bị thực chạy Android 15.

Kiến thức bạn sẽ học được

  • Cách xử lý các thay đổi về chế độ tràn viền trên Android 15.
  • Cách chỉnh sửa để ứng dụng hiển thị tràn viền theo cách tương thích ngược.

Những gì bạn cần

  • Phiên bản Android Studio mới nhất.
  • Một thiết bị kiểm thử hoặc trình mô phỏng chạy Android 15 Beta 1 trở lên.
  • Một SDK cho Android 15 Beta 1 trở lên.

2. Lấy mã khởi đầu

  1. Tải mã khởi đầu xuống từ GitHub.

Ngoài ra, bạn có thể sao chép kho lưu trữ rồi kiểm tra nhánh codelab_improve_android_experience_2024.

$ git clone git@github.com:android/socialite.git
$ cd socialite
$ git checkout codelab_improve_android_experience_2024
  1. Mở SociaLite trong Android Studio rồi chạy ứng dụng này trên thiết bị hoặc trình mô phỏng chạy Android 15. Bạn sẽ thấy một trong những màn hình như sau:

Ứng dụng SociaLite có chế độ thao tác bằng 3 nút.

Ứng dụng SociaLite có chế độ thao tác bằng cử chỉ.

Thao tác bằng 3 nút

Thao tác bằng cử chỉ

Ứng dụng SociaLite trên một thiết bị có màn hình lớn.

Màn hình lớn

  1. Trên trang Chats (Trò chuyện), chọn một trong những cuộc trò chuyện, chẳng hạn như cuộc trò chuyện với chú cún.

Tin nhắn trò chuyện với chú cún có chế độ thao tác bằng 3 nút

Tin nhắn trò chuyện với chú cún có chế độ thao tác bằng cử chỉ

Tin nhắn trò chuyện với chú cún có chế độ thao tác bằng 3 nút

Tin nhắn trò chuyện với chú cún có chế độ thao tác bằng cử chỉ

3. Chỉnh sửa để ứng dụng hiển thị tràn viền trên Android 15

Chế độ tràn viền là gì?

Ứng dụng có thể nằm phía sau thanh hệ thống, mang đến trải nghiệm chỉn chu cho người dùng và khả năng sử dụng toàn bộ không gian màn hình. Đây là quá trình chuyển sang chế độ tràn viền.

Ảnh GIF về một ứng dụng chuyển sang chế độ tràn viền

Cách xử lý các thay đổi về chế độ tràn viền trên Android 15

Trước Android 15, theo mặc định, giao diện người dùng trong ứng dụng của bạn bị hạn chế đặt bố cục để tránh vùng thanh hệ thống, chẳng hạn như thanh trạng thái và thanh điều hướng. Các ứng dụng chọn chuyển sang chế độ hiển thị tràn viền. Tuỳ thuộc vào ứng dụng, việc chọn chuyển sang chế độ hiển thị tràn viền có thể đơn giản hoặc rườm rà.

Kể từ Android 15, theo mặc định, ứng dụng của bạn sẽ hiển thị tràn viền. Bạn sẽ thấy các chế độ mặc định sau:

  • Thanh thao tác bằng 3 nút có màu trong mờ.
  • Thanh thao tác bằng cử chỉ có màu trong suốt.
  • Thanh trạng thái có màu trong suốt.
  • Nếu không áp dụng phần lồng ghép hoặc khoảng đệm, nội dung sẽ nằm phía sau các thanh hệ thống, chẳng hạn như thanh điều hướng, thanh trạng thái và thanh chú thích.

Việc này giúp đảm bảo chế độ hiển thị tràn viền được chú ý để nâng cao chất lượng ứng dụng và giảm tải công việc cần làm để ứng dụng của bạn chuyển sang chế độ hiển thị tràn viền. Tuy nhiên, sự thay đổi này có thể ảnh hưởng tiêu cực đến ứng dụng của bạn. Bạn sẽ thấy 2 ví dụ về sự ảnh hưởng tiêu cực trong SociaLite sau khi nâng cấp SDK mục tiêu lên Android 15.

Thay đổi giá trị SDK mục tiêu thành Android 15

  1. Trong tệp build.gradle của ứng dụng SociaLite, hãy thay đổi phiên bản SDK mục tiêu và SDK biên dịch thành Android 15 hoặc VanillaIceCream.

Nếu bạn tham gia lớp học lập trình này trước khi có bản phát hành ổn định của Android 15, thì mã sẽ có dạng như sau:

android {
    namespace = "com.google.android.samples.socialite"
    compileSdkPreview = "VanillaIceCream"

    defaultConfig {
        applicationId = "com.google.android.samples.socialite"
        minSdk = 21
        targetSdkPreview = "VanillaIceCream"
        ...
    }
...
}

Nếu bạn tham gia lớp học lập trình này sau khi có bản phát hành ổn định của Android 15, thì mã sẽ có dạng như sau:

android {
    namespace = "com.google.android.samples.socialite"
    compileSdk = 35

    defaultConfig {
        applicationId = "com.google.android.samples.socialite"
        minSdk = 21
        targetSdk = 35
        ...
    }
...
}
  1. Tạo lại SociaLite và quan sát những vấn đề sau:
  • Tính năng bảo vệ trong nền của chế độ thao tác bằng 3 nút không khớp với thanh điều hướng. Màn hình Chats (Trò chuyện) hiển thị tràn viền dù bạn không can thiệp thao tác bằng cử chỉ. Tuy nhiên, cần xoá tính năng bảo vệ trong nền của chế độ thao tác bằng 3 nút.

Màn hình Chats (Trò chuyện) có chế độ thao tác bằng 3 nút.

Màn hình Chats (Trò chuyện) có chế độ thao tác bằng cử chỉ.

Màn hình Chats (Trò chuyện) có chế độ thao tác bằng 3 nút

Màn hình Chats (Trò chuyện) có chế độ thao tác bằng cử chỉ

  • Giao diện người dùng bị che khuất. Một cuộc trò chuyện có các phần tử ở cuối giao diện người dùng bị thanh điều hướng che khuất. Vấn đề này xảy ra rõ nhất ở chế độ thao tác bằng 3 nút.

Tin nhắn trò chuyện với chú cún có chế độ thao tác bằng 3 nút.

Tin nhắn trò chuyện với chú cún có chế độ thao tác bằng cử chỉ.

Tin nhắn trò chuyện với chú cún có chế độ thao tác bằng 3 nút

Tin nhắn trò chuyện với chú cún có chế độ thao tác bằng cử chỉ

Khắc phục vấn đề về SociaLite

Để xoá tính năng bảo vệ trong nền của chế độ thao tác bằng 3 nút, hãy làm theo các bước sau:

  1. Trong tệp MainActivity.kt, hãy xoá tính năng bảo vệ trong nền mặc định bằng cách đặt thuộc tính window.isNavigationBarContrastEnforced thành false.
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        installSplashScreen()
        super.onCreate(savedInstanceState)
        setContent {
            // Add this block:
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                window.isNavigationBarContrastEnforced = false
            }
        }
    }
    ...
}

window.isNavigationBarContrastEnforced giúp đảm bảo rằng thanh điều hướng có đủ độ tương phản khi bạn yêu cầu nền hoàn toàn trong suốt. Bằng cách đặt thuộc tính này thành false, bạn đang thiết lập hiệu quả nền chế độ thao tác bằng 3 nút thành trong suốt. window.isNavigationBarContrastEnforced sẽ chỉ tác động đến chế độ thao tác bằng 3 nút mà không tác động đến chế độ thao tác bằng cử chỉ.

  1. Chạy lại ứng dụng và xem một trong những cuộc trò chuyện trên thiết bị Android 15 của bạn. Giờ đây, màn hình Timeline (Dòng thời gian), Chats (Trò chuyện) và Settings (Cài đặt) đều hiển thị tràn viền. NavigationBar của ứng dụng (có nút Timeline (Dòng thời gian), Chats (Trò chuyện) và Settings (Cài đặt)) nằm phía sau thanh điều hướng bằng 3 nút trong suốt của hệ thống.

Màn hình Chats (Trò chuyện) có chế độ thao tác bằng 3 nút và đã xoá phân dải.

Cuộc trò chuyện với chú cún ở chế độ thao tác bằng cử chỉ.

Màn hình Chats (Trò chuyện) đã xoá phân dải

Không có thay đổi nào ở chế độ thao tác bằng cử chỉ

Tuy nhiên, lưu ý rằng InputBar của cuộc trò chuyện vẫn bị thanh hệ thống che khuất. Bạn cần xử lý phần lồng ghép đúng cách để khắc phục vấn đề này.

Cuộc trò chuyện với chú cún ở chế độ thao tác bằng 3 nút.

Cuộc trò chuyện với chú cún ở chế độ thao tác bằng cử chỉ.

Cuộc trò chuyện với chú cún ở chế độ thao tác bằng 3 nút. Trường nhập dữ liệu ở dưới cùng bị thanh điều hướng của hệ thống che khuất.

Cuộc trò chuyện với chú cún ở chế độ thao tác bằng cử chỉ. Trường nhập dữ liệu ở dưới cùng bị thanh điều hướng của hệ thống che khuất.

Trong SociaLite, InputBar bị che khuất. Trên thực tế, bạn có thể thấy các phần tử ở trên cùng, dưới cùng, bên trái và bên phải bị che khuất khi bạn xoay sang chế độ ngang hoặc khi bạn đang dùng một thiết bị có màn hình lớn. Bạn cần xem xét cách xử lý phần lồng ghép trong mọi trường hợp sử dụng này. Đối với SociaLite, bạn áp dụng khoảng đệm để tăng nội dung có thể nhấn của InputBar.

Để áp dụng phần lồng ghép nhằm khắc phục giao diện người dùng bị che khuất, hãy làm theo các bước sau:

  1. Chuyển đến tệp ui/chat/ChatScreen.kt rồi tìm thành phần kết hợp ChatContent ở khoảng dòng 178. Thành phần kết hợp này chứa giao diện người dùng cho màn hình trò chuyện. ChatContent tận dụng Scaffold để dễ dàng tạo giao diện người dùng. Theo mặc định, Scaffold cung cấp thông tin về giao diện người dùng hệ thống, chẳng hạn như độ sâu của thanh hệ thống, phần lồng ghép mà bạn có thể sử dụng cùng với giá trị khoảng đệm của Scaffold (tham số innerPadding). Thêm khoảng đệm vào InputBar bằng innerPadding của Scaffold.
  2. Tìm InputBar trong ChatContent gần dòng 214. Đây là thành phần kết hợp tuỳ chỉnh tạo giao diện người dùng để người dùng soạn tin nhắn. Bản xem trước có dạng như sau:

PreviewInputBar.

InputBar sẽ lấy và áp dụng một contentPadding làm khoảng đệm cho thành phần kết hợp Row chứa phần còn lại của giao diện người dùng. Khoảng đệm này sẽ được áp dụng cho mọi phía của thành phần kết hợp Row. Bạn có thể thấy điều này ở khoảng dòng 432. Dưới đây là thành phần kết hợp InputBar để tham chiếu (không thêm mã này):

// Don't add this code because it's only for reference.
@Composable
private fun InputBar(
    contentPadding: PaddingValues,
    ...,
) {
    Surface(...) {
        Row(
            modifier = Modifier
                .padding(contentPadding)
            ...
        ) {
            IconButton(...) { ... } // take picture
            IconButton(...) { ... } // attach picture
            TextField(...) // write message
            FilledIconButton(...){ ... } // send message
            }
        }
    }
}
  1. Quay lại InputBar trong ChatContent rồi thay đổi contentPadding để sử dụng phần lồng ghép trên thanh hệ thống. Nơi để bạn quay lại nằm ở khoảng dòng 220.
InputBar(
    ...
    contentPadding = innerPadding, //Add this line.
    // contentPadding = PaddingValues(0.dp), // Remove this line.
    ...
 )
  1. Chạy lại ứng dụng trên thiết bị Android 15.

Cuộc trò chuyện với chú cún ở chế độ thao tác bằng 3 nút.

Cuộc trò chuyện với chú cún ở chế độ thao tác bằng cử chỉ.

Cuộc trò chuyện với chú cún ở chế độ thao tác 3 nút áp dụng sai phần lồng ghép.

Cuộc trò chuyện với chú cún ở chế độ thao tác bằng cử chỉ áp dụng sai phần lồng ghép.

Khoảng đệm dưới cùng đã được áp dụng để các nút không bị thanh hệ thống che khuất. Tuy nhiên, khoảng đệm trên cùng cũng sẽ được áp dụng. Khoảng đệm trên cùng bao gồm độ sâu của TopAppBar và thanh hệ thống. Scaffold truyền các giá trị khoảng đệm đến nội dung của nó để có thể tránh thanh ứng dụng trên cùng và thanh hệ thống.

  1. Để khắc phục vấn đề về khoảng đệm trên cùng, hãy tạo một bản sao của innerPadding PaddingValues, đặt khoảng đệm trên cùng thành 0.dp rồi truyền bản sao bạn đã sửa đổi vào contentPadding.
InputBar(
    ...
    contentPadding = innerPadding.copy(layoutDirection, top = 0.dp), //Add this line.
    // contentPadding = innerPadding, // Remove this line.
    ...
 )
  1. Chạy lại ứng dụng trên thiết bị Android 15.

Cuộc trò chuyện với chú cún ở chế độ thao tác bằng 3 nút.

Cuộc trò chuyện với chú cún ở chế độ thao tác bằng cử chỉ.

Cuộc trò chuyện với chú cún ở chế độ thao tác bằng 3 nút áp dụng đúng phần lồng ghép.

Cuộc trò chuyện với chú cún ở chế độ thao tác bằng cử chỉ áp dụng đúng phần lồng ghép.

Xin chúc mừng! Bạn đã điều chỉnh để SociaLite tương thích với các thay đổi về nền tảng hiển thị tràn viền trên Android 15. Tiếp theo, bạn sẽ tìm hiểu cách chỉnh sửa để SociaLite hiển thị tràn viền theo cách tương thích ngược.

4. Chỉnh sửa để SociaLite hiển thị tràn viền theo cách tương thích ngược

Giờ đây, SociaLite đã hiển thị tràn viền trên Android 15 nhưng vẫn chưa hiển thị tràn viền trên các thiết bị Android cũ hơn. Để SociaLite hiển thị tràn viền trên các thiết bị Android cũ hơn, hãy gọi enableEdgeToEdge trước khi thiết lập nội dung này trong tệp MainActivity.kt.

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        installSplashScreen()
        enableEdgeToEdge() // Add this line.
        window.isNavigationBarContrastEnforced = false
        super.onCreate(savedInstanceState)
        setContent {... }
    }
}

Dữ liệu nhập cho enableEdgeToEdgeimport androidx.activity.enableEdgeToEdge. Phần phụ thuộc là AndroidX Activity 1.8.0 trở lên.

Nếu muốn biết thông tin tổng quan chuyên sâu về cách chỉnh sửa để ứng dụng hiển thị tràn viền theo cách tương thích ngược và cách xử lý phần lồng ghép, hãy xem các hướng dẫn sau:

Đến đây là kết thúc phần hiển thị tràn viền trong lộ trình này. Phần tiếp theo là không bắt buộc, trong đó thảo luận về những điểm khác cần cân nhắc đối với chế độ tràn viền có thể áp dụng cho ứng dụng của bạn.

5. Không bắt buộc: Những điểm khác cần cân nhắc về chế độ tràn viền

Xử lý phần lồng ghép trên các cấu trúc

Thành phần

Bạn có thể nhận thấy nhiều thành phần trong SociaLite không chuyển đổi sau khi chúng tôi thay đổi giá trị SDK mục tiêu. SociaLite được cấu trúc bằng các phương pháp hay nhất để việc xử lý thay đổi trên nền tảng này trở nên dễ dàng. Dưới đây là các phương pháp hay nhất:

  • Sử dụng các thành phần Material Design 3 (androidx.compose.material3), chẳng hạn như TopAppBar, BottomAppBarNavigationBar vì chúng tự động áp dụng phần lồng ghép.
  • Nếu ứng dụng của bạn dùng các thành phần Material 2 (androidx.compose.material) trong Compose, thì các thành phần này sẽ không tự động xử lý phần lồng ghép. Tuy nhiên, bạn có thể truy cập vào những phần lồng ghép này và áp dụng chúng theo cách thủ công. Trong androidx.compose.material 1.6.0 trở lên, hãy dùng tham số windowInsets để áp dụng phần lồng ghép theo cách thủ công cho BottomAppBar, TopAppBar, BottomNavigationNavigationRail. Tương tự, hãy dùng tham số contentWindowInsets cho Scaffold. Nếu không, hãy áp dụng phần lồng ghép theo cách thủ công như khoảng đệm.
  • Nếu ứng dụng của bạn dùng các thành phần Khung hiển thị và Material (com.google.android.material), thì hầu hết thành phần Material dựa trên Khung hiển thị (chẳng hạn như BottomNavigationView, BottomAppBar, NavigationRailViewNavigationView) sẽ xử lý phần lồng ghép nên có thể bạn không cần phải làm gì thêm. Tuy nhiên, bạn sẽ cần phải thêm android:fitsSystemWindows="true" nếu dùng AppBarLayout.
  • Nếu ứng dụng của bạn dùng Khung hiển thị BottomSheet, SideSheet hoặc các vùng chứa tuỳ chỉnh, hãy áp dụng khoảng đệm bằng ViewCompat.setOnApplyWindowInsetsListener. Đối với RecyclerView, hãy áp dụng khoảng đệm bằng trình nghe này, đồng thời thêm clipToPadding="false".
  • Dùng Scaffold (hoặc NavigationSuiteScaffold, ListDetailPaneScaffold) chứ không dùng Surface cho một giao diện người dùng phức tạp. Scaffold giúp bạn dễ dàng đặt vị trí cho TopAppBar, BottomAppBar, NavigationBarNavigationRail.

Nội dung có thể cuộn

Ứng dụng của bạn có thể chứa các danh sách và mục cuối cùng trong danh sách có thể bị thanh điều hướng của hệ thống che khuất khi có thay đổi về Android 15.

Ứng dụng có mục cuối cùng trong danh sách bị chế độ thao tác bằng 3 nút che khuất.

Minh hoạ mục cuối cùng trong danh sách bị chế độ thao tác bằng 3 nút che khuất.

Nội dung có thể cuộn bằng Compose

Trong Compose, nếu bạn không sử dụng TextField, hãy dùng contentPadding của LazyColumn để thêm một khoảng trống vào mục cuối cùng:

Scaffold { innerPadding ->
    LazyColumn(
        contentPadding = innerPadding
    ) {
        // Content that does not contain TextField
    }
}

Ứng dụng có mục cuối cùng trong danh sách không bị chế độ thao tác bằng 3 nút che khuất.

Minh hoạ mục cuối cùng trong danh sách không bị chế độ thao tác bằng 3 nút che khuất.

Đối với TextField, hãy dùng một Spacer để vẽ TextField cuối cùng trong LazyColumn. Để biết thêm thông tin, hãy xem phần Sử dụng phần lồng ghép.

LazyColumn(
    Modifier.imePadding()
) {
    // Content with TextField
    item {
        Spacer(
            Modifier.windowInsetsBottomHeight(
                WindowInsets.systemBars
            )
        )
    }
}

Nội dung có thể cuộn bằng Khung hiển thị

Đối với RecyclerView hoặc NestedScrollView, hãy thêm android:clipToPadding="false".

<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/recycler"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipToPadding="false"
    app:layoutManager="LinearLayoutManager" />

Cung cấp khoảng đệm ở bên trái, bên phải và dưới cùng từ phần lồng ghép cửa sổ bằng setOnApplyWindowInsetsListener:

ViewCompat.setOnApplyWindowInsetsListener(binding.recycler) { v, insets ->
    val i = insets.getInsets(
        WindowInsetsCompat.Type.systemBars() + WindowInsetsCompat.Type.displayCutout()
    )
    v.updatePadding(
        left = i.left,
        right = i.right,
        bottom = i.bottom + bottomPadding,
    )
    WindowInsetsCompat.CONSUMED
}

Sử dụng LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS

Trước khi nhắm đến SDK 35, SocialLite có dạng như sau ở chế độ ngang, trong đó viền bên trái có một hộp lớn màu trắng để tính đến vết cắt dành cho camera. Trong chế độ thao tác bằng 3 nút, các nút này ở phía bên phải.

Ứng dụng SociaLite ở chế độ ngang.

Sau khi nhắm đến SDK 35, SociaLite sẽ có dạng như sau, trong đó viền bên trái sẽ không còn có một hộp lớn màu trắng để tính đến vết cắt dành cho camera nữa. Để có được hiệu ứng này, Android tự động thiết lập LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS. Ứng dụng SociaLite ở chế độ ngang.

Tuỳ thuộc vào ứng dụng của mình, bạn nên xử lý phần lồng ghép tại đây.

Để thực hiện việc này trong SociaLite, hãy làm theo các bước sau:

  1. Trong tệp ui/ContactRow.kt, hãy tìm thành phần kết hợp Row.
  2. Sửa đổi khoảng đệm để tính đến vết cắt trên màn hình.
@Composable
fun ChatRow(
   chat: ChatDetail,
   onClick: (() -> Unit)?,
   modifier: Modifier = Modifier,
) {
   // Add layoutDirection, displayCutout, startPadding, and endPadding.
   val layoutDirection = LocalLayoutDirection.current
   val displayCutout = WindowInsets.displayCutout.asPaddingValues()
   val startPadding = displayCutout.calculateStartPadding(layoutDirection)
   val endPadding = displayCutout.calculateEndPadding(layoutDirection)
   Row(
       modifier = modifier
           ...
           // .padding(16.dp) // Remove this line.
           // Add this block:
           .padding(
               PaddingValues(
                   top = 16.dp,
                   bottom = 16.dp,
                   // Ensure content is not occluded by display cutouts
                   // when rotating the device.
                   start = startPadding.coerceAtLeast(16.dp),
                   end = endPadding.coerceAtLeast(16.dp)
               )
           ),
       ...
   ) { ... }

Sau khi xử lý vết cắt trên màn hình, SociaLite có dạng như sau:

Ứng dụng SociaLite ở chế độ ngang.

Bạn có thể kiểm thử nhiều cấu hình vết cắt trên màn hình thông qua màn hình Developer options (Tuỳ chọn dành cho nhà phát triển) trong phần Display cutout (Vết cắt trên màn hình).

Kể từ Android 15 Beta 2, nếu ứng dụng của bạn có một cửa sổ không nổi (ví dụ: một Hoạt động) và đang dùng LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT, LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER hoặc LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES, thì Android sẽ diễn giải những chế độ vết cắt này thành LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS. Trước đây, trong Android 15 Beta 1, ứng dụng của bạn có thể gặp sự cố.

Thanh chú thích cũng là thanh hệ thống

Thanh chú thích cũng là một thanh hệ thống vì thanh này mô tả kiểu trang trí cửa sổ giao diện người dùng hệ thống của cửa sổ dạng tự do, chẳng hạn như thanh tiêu đề trên cùng. Bạn có thể xem thanh chú thích trong một trình mô phỏng máy tính trên Android Studio. Trong ảnh chụp màn hình sau, thanh chú thích nằm ở đầu ứng dụng.

Trình mô phỏng hiện một thanh chú thích.

Trong Compose, nếu bạn đang dùng PaddingValues, safeContent, safeDrawing của Scaffold hoặc WindowInsets.systemBars tích hợp, thì ứng dụng của bạn sẽ hiển thị như mong đợi. Tuy nhiên, nếu bạn đang xử lý phần lồng ghép bằng statusBar, thì nội dung ứng dụng của bạn có thể không hiển thị như mong đợi vì thanh trạng thái không tính đến thanh chú thích.

Trong Khung hiển thị, nếu bạn xử lý phần lồng ghép theo cách thủ công bằng WindowInsetsCompat.systemBars, thì ứng dụng của bạn sẽ hiển thị như mong đợi. Nếu bạn xử lý phần lồng ghép theo cách thủ công bằng WindowInsetsCompat.statusBars, thì có thể ứng dụng của bạn không hiển thị như mong đợi vì thanh trạng thái không phải là thanh chú thích.

Ứng dụng ở chế độ hiển thị tối đa

Các màn hình ở chế độ hiển thị tối đa phần lớn không chịu ảnh hưởng của biện pháp thực thi chế độ tràn viền trên Android 15, vì các ứng dụng hiển thị tối đa đã hiển thị tràn viền.

Bảo vệ thanh hệ thống

Bạn có thể muốn ứng dụng có một thanh trong suốt cho chế độ thao tác bằng cử chỉ, nhưng lại muốn có một thanh trong mờ hoặc không trong suốt cho chế độ thao tác bằng 3 nút.

Trong Android 15, chế độ thao tác bằng 3 nút trong mờ là mặc định vì nền tảng này đặt thuộc tính window.isNavigationBarContrastEnforced thành true. Chế độ thao tác bằng cử chỉ vẫn có màu trong suốt.

Một ứng dụng ở chế độ thao tác bằng 3 nút.

Theo mặc định, chế độ thao tác bằng 3 nút có màu trong mờ.

Nhìn chung, chế độ thao tác bằng 3 nút chỉ cần có màu trong mờ. Tuy nhiên, trong một số trường hợp, có thể ứng dụng của bạn cần có chế độ thao tác bằng 3 nút không trong suốt. Trước tiên, hãy đặt thuộc tính window.isNavigationBarContrastEnforced thành false. Sau đó, dùng WindowInsetsCompat.tappableElement cho Khung hiển thị hoặc WindowInsets.tappableElement cho Compose. Nếu những tham số này bằng 0, tức là người dùng đang sử dụng chế độ thao tác bằng cử chỉ. Nếu những tham số này khác 0, tức là người dùng đang sử dụng chế độ thao tác bằng 3 nút. Nếu người dùng đang sử dụng chế độ thao tác bằng 3 nút, hãy vẽ một khung hiển thị hoặc hộp phía sau thanh điều hướng. Một ví dụ về Compose sẽ có dạng như sau:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            window.isNavigationBarContrastEnforced = false
            MyTheme {
                Surface(...) {
                    MyContent(...)
                    ProtectNavigationBar()
                }
            }
        }
    }
}

// Use only if required.
@Composable
fun ProtectNavigationBar(modifier: Modifier = Modifier) {
   val density = LocalDensity.current
   val tappableElement = WindowInsets.tappableElement
   val bottomPixels = tappableElement.getBottom(density)
   val usingTappableBars = remember(bottomPixels) {
       bottomPixels != 0
   }
   val barHeight = remember(bottomPixels) {
       tappableElement.asPaddingValues(density).calculateBottomPadding()
   }

   Column(
       modifier = modifier.fillMaxSize(),
       verticalArrangement = Arrangement.Bottom
   ) {
       if (usingTappableBars) {
           Box(
               modifier = Modifier
                   .background(MaterialTheme.colorScheme.background)
                   .fillMaxWidth()
                   .height(barHeight)
           )
       }
   }
}

Một ứng dụng ở chế độ thao tác bằng 3 nút.

Chế độ thao tác bằng 3 nút không trong suốt

6. Xem mã giải pháp

Phương thức onCreate của tệp MainActivity.kt sẽ có dạng như sau:

class MainActivity : ComponentActivity() {
   override fun onCreate(savedInstanceState: Bundle?) {
       installSplashScreen()
       enableEdgeToEdge()
       window.isNavigationBarContrastEnforced = false
       super.onCreate(savedInstanceState)
       setContent {
           Main(
               shortcutParams = extractShortcutParams(intent),
           )
       }
   }
}

Thành phần kết hợp ChatContent trong tệp ChatScreen.kt sẽ xử lý phần lồng ghép:

private fun ChatContent(...) {
   ...
   Scaffold(...) { innerPadding ->
       Column {
           ...
           InputBar(
               input = input,
               onInputChanged = onInputChanged,
               onSendClick = onSendClick,
               onCameraClick = onCameraClick,
               onPhotoPickerClick = onPhotoPickerClick,
               contentPadding = innerPadding.copy(
                    layoutDirection, top = 0.dp
                ),
               sendEnabled = sendEnabled,
               modifier = Modifier
                   .fillMaxWidth()
                   .windowInsetsPadding(
                       WindowInsets.ime.exclude(WindowInsets.navigationBars)
                    ),
            )
       }
   }
}

Mã giải pháp có trong nhánh chính. Nếu bạn đã tải SociaLite xuống, mã này sẽ có dạng như sau:

git checkout main

Nếu không, bạn có thể tải lại mã này xuống để xem trực tiếp nhánh chính hoặc thông qua git:

git clone git@github.com:android/socialite.git