Đảm bảo giao diện người dùng của bạn hoạt động với các phần lồng ghép cửa sổ

Sau khi Hoạt động của bạn đã kiểm soát việc xử lý tất cả các phần lồng ghép, bạn có thể sử dụng API Compose để đảm bảo rằng nội dung không bị che khuất và các phần tử có thể tương tác không chồng chéo với giao diện người dùng hệ thống. Các API này cũng đồng bộ hoá bố cục của ứng dụng với các thay đổi về phần lồng ghép.

Ví dụ: đây là phương thức cơ bản nhất để áp dụng phần lồng ghép vào nội dung của toàn bộ ứng dụng:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    enableEdgeToEdge()

    setContent {
        Box(Modifier.safeDrawingPadding()) {
            // the rest of the app
        }
    }
}

Đoạn mã này áp dụng các phần lồng ghép cửa sổ safeDrawing làm khoảng đệm xung quanh toàn bộ nội dung của ứng dụng. Mặc dù điều này đảm bảo rằng các phần tử có thể tương tác không chồng chéo với giao diện người dùng hệ thống, nhưng cũng có nghĩa là không có ứng dụng nào sẽ vẽ phía sau giao diện người dùng hệ thống để đạt được hiệu ứng tràn viền. Để khai thác tối đa toàn bộ cửa sổ, bạn cần điều chỉnh chính xác vị trí áp dụng phần lồng ghép trên cơ sở màn hình hoặc thành phần.

Tất cả các loại phần lồng ghép này đều được tạo ảnh động tự động bằng ảnh động IME được điều chỉnh cho phiên bản cũ xuống API 21. Ngoài ra, tất cả bố cục sử dụng các phần lồng ghép này cũng sẽ tự động tạo ảnh động khi giá trị phần lồng ghép thay đổi.

Có hai cách chính để sử dụng các loại phần lồng ghép này nhằm điều chỉnh bố cục Thành phần kết hợp: đối tượng sửa đổi khoảng đệm và đối tượng sửa đổi kích thước phần lồng ghép.

Đối tượng sửa đổi khoảng đệm

Modifier.windowInsetsPadding(windowInsets: WindowInsets) áp dụng các phần lồng ghép cửa sổ đã cho làm khoảng đệm, hoạt động giống như Modifier.padding. Ví dụ: Modifier.windowInsetsPadding(WindowInsets.safeDrawing) áp dụng các phần lồng ghép bản vẽ an toàn làm khoảng đệm trên cả 4 cạnh.

Ngoài ra, còn có một số phương thức tiện ích tích hợp sẵn cho các loại phần lồng ghép phổ biến nhất. Modifier.safeDrawingPadding() là một trong những phương thức như vậy, tương đương với Modifier.windowInsetsPadding(WindowInsets.safeDrawing). Có các đối tượng sửa đổi tương tự cho các loại phần lồng ghép khác.

Đối tượng sửa đổi kích thước phần lồng ghép

Các đối tượng sửa đổi sau đây áp dụng một số phần lồng ghép cửa sổ bằng cách đặt kích thước của thành phần thành kích thước của phần lồng ghép:

Modifier.windowInsetsStartWidth(windowInsets: WindowInsets)

Áp dụng cạnh bắt đầu của windowInsets làm chiều rộng (như Modifier.width)

Modifier.windowInsetsEndWidth(windowInsets: WindowInsets)

Áp dụng cạnh cuối của windowInsets làm chiều rộng (như Modifier.width)

Modifier.windowInsetsTopHeight(windowInsets: WindowInsets)

Áp dụng cạnh trên của windowInsets làm chiều cao (như Modifier.height)

Modifier.windowInsetsBottomHeight(windowInsets: WindowInsets)

Áp dụng cạnh dưới của windowInsets làm chiều cao (như Modifier.height)

Các đối tượng sửa đổi này đặc biệt hữu ích khi định cỡ Spacer chiếm không gian của phần lồng ghép:

LazyColumn(
    Modifier.imePadding()
) {
    // Other content
    item {
        Spacer(
            Modifier.windowInsetsBottomHeight(
                WindowInsets.systemBars
            )
        )
    }
}

Mức tiêu thụ phần lồng ghép

Các đối tượng sửa đổi khoảng đệm lồng ghép (windowInsetsPadding và các trình trợ giúp như safeDrawingPadding) sẽ tự động sử dụng phần lồng ghép được áp dụng làm khoảng đệm. Khi đi sâu hơn vào cây thành phần, các đối tượng sửa đổi khoảng đệm lồng ghép và đối tượng sửa đổi kích thước lồng ghép sẽ biết rằng một số phần của các phần lồng ghép đã được các đối tượng sửa đổi khoảng đệm lồng ghép bên ngoài sử dụng và tránh sử dụng cùng một phần của các phần lồng ghép nhiều lần, điều này sẽ dẫn đến việc lãng phí quá nhiều không gian.

Đối tượng sửa đổi kích thước phần lồng ghép cũng tránh sử dụng cùng một phần lồng ghép nhiều lần nếu phần lồng ghép đã được sử dụng. Tuy nhiên, vì đang thay đổi kích thước trực tiếp nên các thành phần này không tự sử dụng phần lồng ghép.

Do đó, các đối tượng sửa đổi khoảng đệm lồng nhau sẽ tự động thay đổi lượng khoảng đệm áp dụng cho từng thành phần kết hợp.

Xem xét cùng một ví dụ về LazyColumn như trước, LazyColumn đang được đối tượng sửa đổi imePadding đổi kích thước. Bên trong LazyColumn, mục cuối cùng được định kích thước bằng chiều cao của phần cuối thanh hệ thống:

LazyColumn(
    Modifier.imePadding()
) {
    // Other content
    item {
        Spacer(
            Modifier.windowInsetsBottomHeight(
                WindowInsets.systemBars
            )
        )
    }
}

Khi IME đóng, đối tượng sửa đổi imePadding() sẽ không áp dụng khoảng đệm, vì IME không có chiều cao. Vì đối tượng sửa đổi imePadding() không áp dụng khoảng đệm, nên không có phần lồng ghép nào được sử dụng và chiều cao của Spacer sẽ là kích thước của cạnh dưới cùng của các thanh hệ thống.

Khi IME mở ra, các phần lồng ghép IME sẽ tạo ảnh động để khớp với kích thước của IME và đối tượng sửa đổi imePadding() sẽ bắt đầu áp dụng khoảng đệm dưới cùng để đổi kích thước LazyColumn khi IME mở ra. Khi đối tượng sửa đổi imePadding() bắt đầu áp dụng khoảng đệm dưới cùng, đối tượng này cũng bắt đầu sử dụng số lượng phần lồng ghép đó. Do đó, chiều cao của Spacer bắt đầu giảm, vì một phần khoảng cách cho các thanh hệ thống đã được đối tượng sửa đổi imePadding() áp dụng. Khi đối tượng sửa đổi imePadding() đang áp dụng một khoảng đệm dưới cùng lớn hơn các thanh hệ thống, chiều cao của Spacer sẽ bằng 0.

Khi IME đóng, các thay đổi sẽ diễn ra theo chiều ngược lại: Spacer bắt đầu mở rộng từ chiều cao bằng 0 khi imePadding() áp dụng ít hơn cạnh dưới của các thanh hệ thống, cho đến khi Spacer khớp với chiều cao của cạnh dưới của các thanh hệ thống sau khi IME hoàn toàn biến mất.

Hình 2. Cột tải lười từ cạnh này sang cạnh kia bằng TextField.

Hành vi này được thực hiện thông qua hoạt động giao tiếp giữa tất cả các đối tượng sửa đổi windowInsetsPadding và có thể chịu ảnh hưởng theo một số cách khác.

Modifier.consumeWindowInsets(insets: WindowInsets) cũng sử dụng các phần lồng ghép giống như Modifier.windowInsetsPadding, nhưng không áp dụng các phần lồng ghép đã sử dụng làm khoảng đệm. Điều này rất hữu ích khi kết hợp với các đối tượng sửa đổi kích thước phần lồng ghép để cho các thành phần đồng cấp biết rằng một số phần lồng ghép nhất định đã được sử dụng:

Column(Modifier.verticalScroll(rememberScrollState())) {
    Spacer(Modifier.windowInsetsTopHeight(WindowInsets.systemBars))

    Column(
        Modifier.consumeWindowInsets(
            WindowInsets.systemBars.only(WindowInsetsSides.Vertical)
        )
    ) {
        // content
        Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime))
    }

    Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.systemBars))
}

Modifier.consumeWindowInsets(paddingValues: PaddingValues) hoạt động rất giống với phiên bản có đối số WindowInsets, nhưng sử dụng một PaddingValues tuỳ ý để tiêu thụ. Điều này rất hữu ích để thông báo cho các thành phần con khi khoảng đệm hoặc khoảng cách được cung cấp bởi một số cơ chế khác ngoài đối tượng sửa đổi khoảng đệm lồng ghép, chẳng hạn như Modifier.padding thông thường hoặc khoảng đệm có chiều cao cố định:

Column(Modifier.padding(16.dp).consumeWindowInsets(PaddingValues(16.dp))) {
    // content
    Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime))
}

Trong trường hợp cần phần lồng ghép cửa sổ thô mà không cần tiêu thụ, hãy sử dụng trực tiếp các giá trị WindowInsets hoặc sử dụng WindowInsets.asPaddingValues() để trả về PaddingValues của các phần lồng ghép không bị ảnh hưởng bởi mức tiêu thụ. Tuy nhiên, do các lưu ý dưới đây, bạn nên sử dụng đối tượng sửa đổi khoảng đệm lồng ghép cửa sổ và đối tượng sửa đổi kích thước lồng ghép cửa sổ bất cứ khi nào có thể.

Các giai đoạn Jetpack Compose và phần lồng ghép

Compose sử dụng các API cốt lõi AndroidX cơ bản để cập nhật và tạo ảnh động cho phần lồng ghép, trong đó sử dụng các API nền tảng cơ bản để quản lý phần lồng ghép. Do hành vi của nền tảng đó, các phần lồng ghép có mối quan hệ đặc biệt với các giai đoạn của Jetpack Compose.

Giá trị của phần lồng ghép được cập nhật sau giai đoạn kết hợp, nhưng trước giai đoạn bố cục. Điều này có nghĩa là việc đọc giá trị của phần lồng ghép trong thành phần kết hợp thường sử dụng giá trị của phần lồng ghép trễ một khung hình. Các đối tượng sửa đổi tích hợp được mô tả trên trang này được tạo để trì hoãn việc sử dụng các giá trị của phần lồng ghép cho đến giai đoạn bố cục, đảm bảo rằng các giá trị lồng ghép được sử dụng trên cùng một khung khi được cập nhật.