WindowInsets là API tiêu chuẩn trong Jetpack Compose để xử lý các vùng trên màn hình bị giao diện người dùng hệ thống che khuất một phần hoặc toàn bộ. Các vùng này bao gồm thanh trạng thái, thanh điều hướng và bàn phím ảo. Ngoài ra, bạn có
thể truyền WindowInsetsRulers được xác định trước như
SafeDrawing đến Modifier.fitInside hoặc Modifier.fitOutside
để căn chỉnh nội dung với các thanh hệ thống và vết cắt trên màn hình hoặc tạo
WindowInsetsRulers tuỳ chỉnh.
Ưu điểm của WindowInsetsRulers
- Tránh sự phức tạp khi sử dụng: API này hoạt động trong giai đoạn đặt vị trí
của bố cục. Điều này có nghĩa là API này hoàn toàn bỏ qua chuỗi sử dụng phần lồng ghép và luôn có thể cung cấp vị trí tuyệt đối, chính xác của các thanh hệ thống và vết cắt trên màn hình, bất kể bố cục mẹ đã thực hiện những gì. Việc sử dụng các phương thức
Modifier.fitInsidehoặcModifier.fitOutsidesẽ giúp khắc phục các vấn đề khi các thành phần kết hợp tổ tiên sử dụng phần lồng ghép không chính xác. - Dễ dàng tránh các thanh hệ thống: API này giúp nội dung ứng dụng của bạn tránh các thanh hệ thống
và vết cắt trên màn hình, đồng thời có thể đơn giản hơn so với việc sử dụng
WindowInsetstrực tiếp. - Có khả năng tuỳ chỉnh cao: Nhà phát triển có thể căn chỉnh nội dung theo các thước đo tuỳ chỉnh và kiểm soát chính xác bố cục của họ bằng các bố cục tuỳ chỉnh.
Nhược điểm của WindowInsetsRulers
- Không thể dùng để đo lường: Vì API này hoạt động trong giai đoạn đặt vị trí, nên thông tin vị trí mà API này cung cấp sẽ không có sẵn trong giai đoạn đo lường trước đó.
Căn chỉnh nội dung bằng các phương thức của thành phần sửa đổi
Modifier.fitInside cho phép các ứng dụng căn chỉnh nội dung theo các thanh hệ thống và vết cắt trên màn hình. Bạn có thể dùng API này thay cho WindowInsets.
Modifier.fitOutside thường là phần đối lập của Modifier.fitInside.
Ví dụ: để xác minh rằng nội dung ứng dụng tránh các thanh hệ thống và vết cắt trên màn hình, bạn có thể sử dụng fitInside(WindowInsetsRulers.safeDrawing.current).
@Composable fun FitInsideDemo(modifier: Modifier) { Box( modifier = modifier .fillMaxSize() // Or DisplayCutout, Ime, NavigationBars, StatusBar, etc... .fitInside(WindowInsetsRulers.SafeDrawing.current) ) }
Bảng sau đây cho biết nội dung ứng dụng của bạn sẽ trông như thế nào với các thước đo được xác định trước bằng Modifier.fitInside hoặc Modifier.fitOutside.
| Loại thước đo được xác định trước | ||
|---|---|---|
![]() |
![]() |
|
![]() |
Không áp dụng |
|
![]() |
![]() |
|
![]() |
Không áp dụng (thay vào đó, hãy sử dụng |
|
![]() |
![]() |
Để sử dụng Modifier.fitInside và Modifier.fitOutside, bạn phải ràng buộc các thành phần kết hợp. Điều này có nghĩa là bạn phải xác định các thành phần sửa đổi như Modifier.size hoặc Modifier.fillMaxSize.
Một số thước đo như Modifier.fitOutside trên SafeDrawing và SystemBars trả về nhiều thước đo. Trong trường hợp này, Android sẽ đặt thành phần kết hợp bằng một thước đo từ trái, trên cùng, phải, dưới cùng.
Tránh IME bằng Modifier.fitInside
Để xử lý các phần tử ở dưới cùng bằng IME bằng Modifier.fitInside, hãy truyền RectRuler nhận giá trị trong cùng của NavigationBar và Ime.
@Composable fun FitInsideWithImeDemo(modifier: Modifier) { Box( modifier = modifier .fillMaxSize() .fitInside( RectRulers.innermostOf( WindowInsetsRulers.NavigationBars.current, WindowInsetsRulers.Ime.current ) ) ) { TextField( value = "Demo IME Insets", onValueChange = {}, modifier = modifier.align(Alignment.BottomStart).fillMaxWidth() ) } }
Tránh thanh trạng thái và thanh chú thích bằng Modifier.fitInside
Tương tự, để xác minh các phần tử ở trên cùng tránh thanh trạng thái và thanh chú thích cùng với Modifier.fitInsider, hãy truyền RectRuler nhận giá trị trong cùng của StatusBars và CaptionBar.
@Composable fun FitInsideWithStatusAndCaptionBarDemo(modifier: Modifier) { Box( modifier = modifier .fillMaxSize() .fitInside( RectRulers.innermostOf( WindowInsetsRulers.StatusBars.current, WindowInsetsRulers.CaptionBar.current ) ) ) }
Tạo WindowInsetsRulers tuỳ chỉnh
Bạn có thể căn chỉnh nội dung theo các thước đo tuỳ chỉnh. Ví dụ: hãy xem xét trường hợp sử dụng trong đó một thành phần kết hợp mẹ xử lý không đúng cách các phần lồng ghép, gây ra vấn đề về khoảng đệm ở một thành phần con ở hạ nguồn. Mặc dù bạn có thể giải quyết vấn đề này theo những cách khác, bao gồm cả việc sử dụng Modifier.fitInside, nhưng bạn cũng có thể tạo một thước đo tuỳ chỉnh để căn chỉnh chính xác thành phần kết hợp con mà không cần khắc phục vấn đề ở thành phần mẹ ở thượng nguồn như trong ví dụ và video sau:
@Composable fun WindowInsetsRulersDemo(modifier: Modifier) { Box( contentAlignment = BottomCenter, modifier = modifier .fillMaxSize() // The mistake that causes issues downstream, as .padding doesn't consume insets. // While it's correct to instead use .windowInsetsPadding(WindowInsets.navigationBars), // assume it's difficult to identify this issue to see how WindowInsetsRulers can help. .padding(WindowInsets.navigationBars.asPaddingValues()) ) { TextField( value = "Demo IME Insets", onValueChange = {}, modifier = modifier // Use alignToSafeDrawing() instead of .imePadding() to precisely place this child // Composable without having to fix the parent upstream. .alignToSafeDrawing() // .imePadding() // .fillMaxWidth() ) } } fun Modifier.alignToSafeDrawing(): Modifier { return layout { measurable, constraints -> if (constraints.hasBoundedWidth && constraints.hasBoundedHeight) { val placeable = measurable.measure(constraints) val width = placeable.width val height = placeable.height layout(width, height) { val bottom = WindowInsetsRulers.SafeDrawing.current.bottom .current(0f).roundToInt() - height val right = WindowInsetsRulers.SafeDrawing.current.right .current(0f).roundToInt() val left = WindowInsetsRulers.SafeDrawing.current.left .current(0f).roundToInt() measurable.measure(Constraints.fixed(right - left, height)) .place(left, bottom) } } else { val placeable = measurable.measure(constraints) layout(placeable.width, placeable.height) { placeable.place(0, 0) } } } }
Video sau đây cho thấy ví dụ về việc sử dụng phần lồng ghép IME có vấn đề do thành phần mẹ ở thượng nguồn trong hình ảnh bên trái và sử dụng các thước đo tuỳ chỉnh để khắc phục vấn đề ở bên phải. Khoảng đệm bổ sung xuất hiện bên dưới thành phần kết hợp TextField vì khoảng đệm của thanh điều hướng không được thành phần mẹ sử dụng. Thành phần con được đặt ở đúng vị trí trong hình ảnh bên phải bằng một thước đo tuỳ chỉnh như trong mã mẫu trước đó.
Xác minh rằng các thành phần mẹ bị ràng buộc
Để sử dụng WindowInsetsRulers một cách an toàn, hãy đảm bảo rằng thành phần mẹ cung cấp các ràng buộc hợp lệ. Thành phần mẹ phải có kích thước được xác định và không thể phụ thuộc vào kích thước của thành phần con sử dụng WindowInsetsRulers. Sử dụng fillMaxSize hoặc các thành phần sửa đổi kích thước khác trên các thành phần kết hợp mẹ.
Tương tự, việc đặt một thành phần kết hợp sử dụng WindowInsetsRulers bên trong một vùng chứa có thể cuộn như verticalScroll có thể gây ra hành vi không mong muốn vì vùng chứa có thể cuộn cung cấp các ràng buộc về chiều cao không giới hạn, không tương thích với logic của thước đo.







