Khi các dịch vụ hỗ trợ tiếp cận di chuyển qua các phần tử trên màn hình, điều quan trọng là các phần tử này phải được nhóm, tách biệt hoặc thậm chí ẩn ở độ chi tiết phù hợp. Khi mỗi một thành phần có thể kết hợp cấp thấp trên màn hình đều được làm nổi bật một cách độc lập, người dùng sẽ phải tương tác nhiều để di chuyển trên màn hình. Nếu các phần tử hợp nhất với nhau quá thường xuyên, thì người dùng có thể không hiểu những phần tử nào thuộc về nhau một cách hợp lý. Nếu có các phần tử trên màn hình chỉ mang tính chất trang trí, thì các phần tử này có thể bị ẩn khỏi các dịch vụ hỗ trợ tiếp cận. Trong những trường hợp này, bạn có thể sử dụng API Compose để hợp nhất, xoá và ẩn ngữ nghĩa.
Ngữ nghĩa hợp nhất
Khi bạn áp dụng đối tượng sửa đổi clickable
cho một thành phần kết hợp mẹ, Compose sẽ tự động hợp nhất tất cả các phần tử con trong đó. Để hiểu cách các thành phần Material và Foundation tương tác trong Compose sử dụng các chiến lược hợp nhất theo mặc định, hãy xem phần Thành phần tương tác.
Thông thường, một thành phần sẽ bao gồm nhiều thành phần kết hợp. Các thành phần kết hợp này có thể tạo thành một nhóm logic và mỗi thành phần có thể chứa thông tin quan trọng, nhưng bạn vẫn có thể muốn dịch vụ hỗ trợ tiếp cận xem chúng là một phần tử.
Ví dụ: hãy nghĩ đến một thành phần kết hợp hiển thị hình đại diện, tên và một số thông tin bổ sung của người dùng:

Bạn có thể bật Compose để hợp nhất các phần tử này bằng cách sử dụng tham số mergeDescendants
trong công cụ sửa đổi ngữ nghĩa. Bằng cách này, các dịch vụ hỗ trợ tiếp cận sẽ coi thành phần này là một thực thể và tất cả các thuộc tính ngữ nghĩa của các thành phần con đều được hợp nhất:
@Composable private fun PostMetadata(metadata: Metadata) { // Merge elements below for accessibility purposes Row(modifier = Modifier.semantics(mergeDescendants = true) {}) { Image( imageVector = Icons.Filled.AccountCircle, contentDescription = null // decorative ) Column { Text(metadata.author.name) Text("${metadata.date} • ${metadata.readTimeMinutes} min read") } } }
Giờ đây, các dịch vụ Hỗ trợ tiếp cận sẽ tập trung vào toàn bộ vùng chứa cùng một lúc và hợp nhất nội dung của các vùng chứa đó:

Mỗi thuộc tính ngữ nghĩa có một chiến lược hợp nhất xác định. Ví dụ: thuộc tính ContentDescription
thêm tất cả các giá trị ContentDescription
con vào một danh sách. Bạn có thể kiểm tra chiến lược hợp nhất của một thuộc tính ngữ nghĩa bằng cách kiểm tra cách triển khai mergePolicy
của thuộc tính đó trong SemanticsProperties.kt.
Các thuộc tính có thể nhận giá trị gốc hoặc giá trị con, hợp nhất các giá trị thành danh sách hoặc chuỗi, hoàn toàn không cho phép hợp nhất và gửi ngoại lệ hoặc bất kỳ chiến lược hợp nhất tuỳ chỉnh nào khác.
Có những trường hợp khác mà bạn muốn hợp nhất ngữ nghĩa con vào ngữ nghĩa mẹ, nhưng điều đó không xảy ra. Trong ví dụ sau, chúng ta có mục danh sách mẹ clickable
với các phần tử con và chúng ta có thể mong đợi mục mẹ hợp nhất tất cả các phần tử con đó:

@Composable private fun ArticleListItem( openArticle: () -> Unit, addToBookmarks: () -> Unit, ) { Row(modifier = Modifier.clickable { openArticle() }) { // Merges with parent clickable: Icon( painter = painterResource(R.drawable.ic_logo), contentDescription = "Article thumbnail" ) ArticleDetails() // Defies the merge due to its own clickable: BookmarkButton(onClick = addToBookmarks) } }
Khi người dùng nhấn vào mục clickable
Row
, bài viết sẽ mở ra. Bên trong, có một BookmarkButton
để đánh dấu trang bài viết. Nút lồng này sẽ xuất hiện dưới dạng chưa hợp nhất, trong khi nội dung con còn lại bên trong hàng sẽ được hợp nhất:

Row
. Cây chưa hợp nhất chứa các nút riêng cho mỗi thành phần kết hợp Text
.Theo thiết kế, một số thành phần kết hợp không tự động được hợp nhất trong thành phần mẹ. Thành phần mẹ không thể hợp nhất các thành phần con khi các thành phần con cũng đang hợp nhất, cho dù là do thiết lập mergeDescendants = true
một cách rõ ràng hay do là các thành phần tự hợp nhất, chẳng hạn như các nút hoặc thành phần có thể nhấp. Việc biết cách một số API nhất định hợp nhất hoặc không hợp nhất có thể giúp bạn gỡ lỗi một số hành vi có thể xảy ra ngoài dự kiến.
Sử dụng tính năng hợp nhất khi các phần tử con tạo thành một nhóm hợp lý và hợp lý trong phần tử mẹ. Tuy nhiên, nếu các phần tử con lồng nhau cần điều chỉnh hoặc xoá ngữ nghĩa của riêng chúng theo cách thủ công, thì các API khác có thể phù hợp hơn với nhu cầu của bạn (ví dụ: clearAndSetSemantics
).
Xoá và đặt ngữ nghĩa
Nếu cần xoá hoặc ghi đè hoàn toàn thông tin ngữ nghĩa, bạn có thể sử dụng API mạnh mẽ là clearAndSetSemantics
.
Khi một thành phần cần xoá ngữ nghĩa của chính nó và các thành phần con, hãy sử dụng API này với một lambda trống. Khi phải ghi đè ngữ nghĩa của biểu thức lambda, hãy đưa nội dung mới vào bên trong biểu thức lambda.
Xin lưu ý rằng khi xoá bằng một lambda trống, ngữ nghĩa đã xoá sẽ không được gửi đến bất kỳ đối tượng sử dụng nào sử dụng thông tin này, chẳng hạn như hỗ trợ tiếp cận, tự động điền hoặc kiểm thử. Khi ghi đè nội dung bằng clearAndSetSemantics{/*semantic information*/}
, ngữ nghĩa mới sẽ thay thế tất cả ngữ nghĩa trước đó của phần tử và các phần tử con cháu của phần tử đó.
Sau đây là ví dụ về thành phần bật/tắt tuỳ chỉnh, được biểu thị bằng một hàng có thể tương tác với biểu tượng và văn bản:
// Developer might intend this to be a toggleable. // Using `clearAndSetSemantics`, on the Row, a clickable modifier is applied, // a custom description is set, and a Role is applied. @Composable fun FavoriteToggle() { val checked = remember { mutableStateOf(true) } Row( modifier = Modifier .toggleable( value = checked.value, onValueChange = { checked.value = it } ) .clearAndSetSemantics { stateDescription = if (checked.value) "Favorited" else "Not favorited" toggleableState = ToggleableState(checked.value) role = Role.Switch }, ) { Icon( imageVector = Icons.Default.Favorite, contentDescription = null // not needed here ) Text("Favorite?") } }
Mặc dù biểu tượng và văn bản có một số thông tin ngữ nghĩa, nhưng chúng không cho biết thành phần này có thể bật/tắt. Việc hợp nhất là chưa đủ vì bạn phải cung cấp thêm thông tin về thành phần.
Vì đoạn mã trên tạo một thành phần bật/tắt tuỳ chỉnh, nên bạn cần thêm tính năng bật/tắt cũng như ngữ nghĩa stateDescription
, toggleableState
và role
. Bằng cách này, trạng thái thành phần và thao tác liên kết sẽ có sẵn. Ví dụ: TalkBack thông báo "Nhấn đúp để bật/tắt" thay vì "Nhấn đúp để kích hoạt".
Bằng cách xoá ngữ nghĩa ban đầu và đặt ngữ nghĩa mới, mô tả rõ ràng hơn, các dịch vụ hỗ trợ tiếp cận hiện có thể thấy rằng đây là một thành phần có thể bật/tắt và có thể thay đổi trạng thái.
Khi sử dụng clearAndSetSemantics
, hãy cân nhắc những điều sau:
- Vì các dịch vụ không nhận được thông tin nào khi bạn đặt API này, nên bạn nên sử dụng API này một cách tiết kiệm.
- Các tác nhân AI và các dịch vụ tương tự có thể sử dụng thông tin ngữ nghĩa để hiểu màn hình, do đó, bạn chỉ nên xoá thông tin này khi cần thiết.
- Bạn có thể đặt ngữ nghĩa tuỳ chỉnh trong lambda API.
- Thứ tự của đối tượng sửa đổi rất quan trọng – API này xoá tất cả ngữ nghĩa sau vị trí áp dụng, bất kể các chiến lược hợp nhất khác.
Ẩn ngữ nghĩa
Trong một số trường hợp, các thành phần không cần được gửi đến dịch vụ hỗ trợ tiếp cận – có thể thông tin bổ sung của các thành phần này không cần thiết cho khả năng hỗ trợ tiếp cận, hoặc thông tin đó chỉ mang tính chất trang trí và không tương tác. Trong những trường hợp này, bạn có thể ẩn các phần tử bằng API hideFromAccessibility
.
Trong các ví dụ sau đây là các thành phần có thể cần phải ẩn: một watermark thừa trải dài một thành phần và một ký tự dùng để trang trí thông tin riêng biệt.
@Composable fun WatermarkExample( watermarkText: String, content: @Composable () -> Unit, ) { Box { WatermarkedContent() // Mark the watermark as hidden to accessibility services. WatermarkText( text = watermarkText, color = Color.Gray.copy(alpha = 0.5f), modifier = Modifier .align(Alignment.BottomEnd) .semantics { hideFromAccessibility() } ) } } @Composable fun DecorativeExample() { Text( modifier = Modifier.semantics { hideFromAccessibility() }, text = "A dot character that is used to decoratively separate information, like •" ) }
Việc sử dụng hideFromAccessibility
ở đây đảm bảo rằng hình mờ và trang trí sẽ bị ẩn khỏi các dịch vụ hỗ trợ tiếp cận, nhưng vẫn giữ lại ngữ nghĩa của chúng cho các trường hợp sử dụng khác, chẳng hạn như kiểm thử.
Thông tin chi tiết về các trường hợp sử dụng
Dưới đây là bản tóm tắt các trường hợp sử dụng để hiểu cách phân biệt rõ ràng giữa các API trước:
- Khi nội dung không được các dịch vụ hỗ trợ tiếp cận sử dụng:
- Sử dụng
hideFromAccessibility
khi nội dung có thể là nội dung trang trí hoặc thừa nhưng vẫn phải được kiểm thử. - Sử dụng
clearAndSetSemantics{}
với một lambda trống khi cần xoá ngữ nghĩa của phần tử mẹ và con cho tất cả các dịch vụ. - Sử dụng
clearAndSetSemantics{/*content*/}
với nội dung bên trong lambda khi cần đặt ngữ nghĩa của một thành phần theo cách thủ công.
- Sử dụng
- Trường hợp nội dung được coi là một thực thể và cần có đầy đủ thông tin của tất cả phần tử con:
- Sử dụng các phần tử con ngữ nghĩa hợp nhất.

Đề xuất cho bạn
- Lưu ý: văn bản có đường liên kết sẽ hiện khi JavaScript tắt
- Hỗ trợ tiếp cận trong Compose
- [Material Design 2 trong Compose][19]
- Kiểm thử bố cục Compose