Material, giao diện người dùng Compose và các API Nền tảng triển khai và cung cấp nhiều phương pháp hỗ trợ tiếp cận theo mặc định. Chúng chứa ngữ nghĩa tích hợp tuân theo vai trò và chức năng cụ thể của chúng. Điều này có nghĩa là hầu hết các tính năng hỗ trợ tiếp cận đều được cung cấp mà không cần hoặc chỉ cần thêm một chút công sức.
Việc sử dụng các API phù hợp cho mục đích phù hợp có nghĩa là các thành phần thường đi kèm với các hành vi hỗ trợ tiếp cận được xác định trước, bao gồm các trường hợp sử dụng tiêu chuẩn. Tuy nhiên, hãy luôn kiểm tra kỹ xem những chế độ mặc định này có phù hợp với nhu cầu hỗ trợ tiếp cận của bạn hay không. Nếu không, Compose sẽ cung cấp các cách để đáp ứng những yêu cầu cụ thể hơn.
Việc hiểu rõ ngữ nghĩa và mẫu hỗ trợ tiếp cận mặc định trong các API Compose sẽ giúp bạn sử dụng chúng một cách hiệu quả. Điều này cũng giúp bạn hỗ trợ khả năng tiếp cận trong nhiều thành phần tuỳ chỉnh hơn.
Kích thước đích chạm tối thiểu
Mọi phần tử trên màn hình mà người dùng có thể nhấp, chạm vào hoặc tương tác đều phải đủ lớn để tương tác một cách đáng tin cậy. Khi xác định kích thước các phần tử này, hãy đảm bảo đặt kích thước tối thiểu là 48dp để tuân thủ đúng Nguyên tắc hỗ trợ tiếp cận của Material Design.
Các thành phần Material (chẳng hạn như Checkbox, RadioButton, Switch, Slider và Surface) đặt kích thước tối thiểu này nội bộ, nhưng chỉ khi thành phần có thể nhận được thao tác của người dùng. Ví dụ: khi tham số onCheckedChange của Checkbox được đặt thành giá trị không rỗng, hộp đánh dấu sẽ bao gồm khoảng đệm để có chiều rộng và chiều cao ít nhất là 48 dp.
@Composable private fun CheckableCheckbox() { Checkbox(checked = true, onCheckedChange = {}) }
 
  Khi tham số onCheckedChange được đặt thành rỗng, khoảng đệm sẽ không được thêm vào vì không thể tương tác trực tiếp với thành phần này.
@Composable private fun NonClickableCheckbox() { Checkbox(checked = true, onCheckedChange = null) }
 
  Khi triển khai các chế độ kiểm soát lựa chọn như Switch, RadioButton hoặc Checkbox, bạn thường nâng hành vi có thể nhấp lên vùng chứa mẹ bằng cách đặt lệnh gọi lại lượt nhấp trên thành phần kết hợp thành null rồi thêm một đối tượng sửa đổi toggleable hoặc selectable cho thành phần kết hợp mẹ.
@Composable private fun CheckableRow() { MaterialTheme { var checked by remember { mutableStateOf(false) } Row( Modifier .toggleable( value = checked, role = Role.Checkbox, onValueChange = { checked = !checked } ) .padding(16.dp) .fillMaxWidth() ) { Text("Option", Modifier.weight(1f)) Checkbox(checked = checked, onCheckedChange = null) } } }
 
  Khi kích thước của một thành phần kết hợp có thể nhấp nhỏ hơn kích thước đích chạm tối thiểu, Compose vẫn tăng kích thước đích chạm. Compose làm như vậy bằng cách mở rộng kích thước đích chạm nằm ngoài phạm vi của thành phần có thể kết hợp.
Ví dụ sau đây chứa một Box rất nhỏ có thể nhấp. Khu vực đích chạm tự động mở rộng ra ngoài phạm vi của Box, vì vậy, việc nhấn vào bên cạnh Box sẽ vẫn kích hoạt sự kiện nhấp.
@Composable private fun SmallBox() { var clicked by remember { mutableStateOf(false) } Box( Modifier .size(100.dp) .background(if (clicked) Color.DarkGray else Color.LightGray) ) { Box( Modifier .align(Alignment.Center) .clickable { clicked = !clicked } .background(Color.Black) .size(1.dp) ) } }
 
  Để ngăn chặn sự trùng lặp có thể giữa các khu vực cảm ứng của các thành phần kết hợp, hãy luôn sử dụng kích thước tối thiểu đủ lớn cho thành phần kết hợp. Trong ví dụ này, điều đó có nghĩa là sử dụng đối tượng sửa đổi sizeIn để đặt kích thước tối thiểu cho hộp bên trong:
@Composable private fun LargeBox() { var clicked by remember { mutableStateOf(false) } Box( Modifier .size(100.dp) .background(if (clicked) Color.DarkGray else Color.LightGray) ) { Box( Modifier .align(Alignment.Center) .clickable { clicked = !clicked } .background(Color.Black) .sizeIn(minWidth = 48.dp, minHeight = 48.dp) ) } }
 
  Phần tử đồ hoạ
Khi bạn xác định một thành phần kết hợp Image hoặc Icon, không có cách tự động nào để khung Android hiểu nội dung mà ứng dụng đang hiển thị. Bạn cần truyền mô tả dạng văn bản của phần tử đồ hoạ.
Hãy tưởng tượng một màn hình mà người dùng có thể chia sẻ trang hiện tại với bạn bè. Màn hình này chứa biểu tượng chia sẻ có thể nhấp:
 
  Chỉ dựa vào biểu tượng này, khung Android sẽ không thể mô tả cho người dùng khiếm thị. Khung Android cần có thêm mô tả bằng văn bản của biểu tượng đó.
Tham số contentDescription mô tả một phần tử đồ hoạ. Sử dụng một chuỗi đã được bản địa hoá vì người dùng có thể nhìn thấy chuỗi này.
@Composable private fun ShareButton(onClick: () -> Unit) { IconButton(onClick = onClick) { Icon( imageVector = Icons.Filled.Share, contentDescription = stringResource(R.string.label_share) ) } }
Một số phần tử đồ hoạ chỉ mang tính chất trang trí và bạn có thể không muốn thông báo cho người dùng. Khi đặt tham số contentDescription thành null, bạn chỉ báo cho khung Android rằng phần tử này không có hành động hoặc trạng thái liên kết.
@Composable private fun PostImage(post: Post, modifier: Modifier = Modifier) { val image = post.imageThumb ?: painterResource(R.drawable.placeholder_1_1) Image( painter = image, // Specify that this image has no semantic meaning contentDescription = null, modifier = modifier .size(40.dp, 40.dp) .clip(MaterialTheme.shapes.small) ) }
contentDescription chủ yếu được dùng cho các phần tử đồ hoạ, chẳng hạn như hình ảnh. Các thành phần Material (chẳng hạn như Button hoặc Text) và các hành vi có thể thao tác (chẳng hạn như clickable hoặc toggleable) đi kèm với các ngữ nghĩa được xác định trước khác mô tả hành vi vốn có của chúng và có thể thay đổi thông qua các API Compose khác.
Yếu tố tương tác
Các API Material và Foundation Compose tạo ra những phần tử trên giao diện người dùng mà người dùng có thể tương tác thông qua các API đối tượng sửa đổi clickable và toggleable. Vì các thành phần có thể tương tác có thể bao gồm nhiều phần tử, nên clickable và toggleable hợp nhất ngữ nghĩa của các phần tử con theo mặc định, để thành phần được coi là một thực thể logic.
Ví dụ: Button Material có thể bao gồm một biểu tượng con và một số văn bản. Thay vì coi các thành phần con là các thành phần riêng lẻ, Button Material hợp nhất ngữ nghĩa của các thành phần con theo mặc định, để các dịch vụ hỗ trợ tiếp cận có thể nhóm các thành phần đó cho phù hợp:
 
  Tương tự, việc sử dụng đối tượng sửa đổi clickable cũng khiến một thành phần kết hợp hợp nhất ngữ nghĩa của các thành phần con vào một thực thể duy nhất. Thực thể này được gửi đến các dịch vụ hỗ trợ tiếp cận cùng với một biểu thị hành động tương ứng:
Row( // Uses `mergeDescendants = true` under the hood modifier = Modifier.clickable { openArticle() } ) { Icon( painter = painterResource(R.drawable.ic_logo), contentDescription = "Open", ) Text("Accessibility in Compose") }
Bạn cũng có thể đặt một onClickLabel cụ thể trên thành phần có thể nhấp gốc để cung cấp thông tin bổ sung cho các dịch vụ hỗ trợ tiếp cận và đưa ra một bản trình bày tinh tế hơn về thao tác:
Row( modifier = Modifier .clickable(onClickLabel = "Open this article") { openArticle() } ) { Icon( painter = painterResource(R.drawable.ic_logo), contentDescription = "Open" ) Text("Accessibility in Compose") }
Lấy TalkBack làm ví dụ, bộ sửa đổi clickable này và nhãn nhấp của bộ sửa đổi sẽ cho phép TalkBack đưa ra gợi ý về thao tác "Nhấn đúp để mở bài viết này", thay vì phản hồi mặc định chung chung hơn là "Nhấn đúp để kích hoạt".
Nội dung phản hồi này sẽ thay đổi tuỳ thuộc vào loại hành động. Thao tác nhấn và giữ sẽ cung cấp cho TalkBack một gợi ý "Nhấn đúp và giữ để", theo sau là một nhãn:
Row( modifier = Modifier .combinedClickable( onLongClickLabel = "Bookmark this article", onLongClick = { addToBookmarks() }, onClickLabel = "Open this article", onClick = { openArticle() }, ) ) {}
Trong một số trường hợp, bạn có thể không có quyền truy cập trực tiếp vào đối tượng sửa đổi clickable (ví dụ: khi đối tượng này được đặt ở một vị trí nào đó trong lớp lồng ghép thấp hơn), nhưng vẫn muốn thay đổi nhãn thông báo từ nhãn mặc định. Để làm việc này, hãy tách việc đặt clickable khỏi việc sửa đổi thông báo bằng cách sử dụng đối tượng sửa đổi semantics và đặt nhãn lượt nhấp ở đó để sửa đổi cách biểu thị hành động:
@Composable private fun ArticleList(openArticle: () -> Unit) { NestedArticleListItem( // Clickable is set separately, in a nested layer: onClickAction = openArticle, // Semantics are set here: modifier = Modifier.semantics { onClick( label = "Open this article", action = { // Not needed here: openArticle() true } ) } ) }
Bạn không cần truyền hành động nhấp hai lần. Các API hiện có của Compose, chẳng hạn như clickable hoặc Button, sẽ xử lý việc này cho bạn. Logic hợp nhất xác minh rằng nhãn và thao tác của đối tượng sửa đổi ngoài cùng được thực hiện cho thông tin hiện có. Trong ví dụ trước, NestedArticleListItem sẽ tự động truyền thao tác nhấp vào openArticle() đến ngữ nghĩa clickable của thao tác đó. Bạn có thể để hành động nhấp chuột là rỗng trong hành động của công cụ sửa đổi ngữ nghĩa thứ hai. Tuy nhiên, nhãn lượt nhấp được lấy từ bộ sửa đổi ngữ nghĩa thứ hai onClick(label = "Open this document") vì nhãn này không có trong bộ sửa đổi đầu tiên.
Bạn có thể gặp phải trường hợp mà bạn mong đợi ngữ nghĩa của các thành phần con sẽ được hợp nhất vào một thành phần mẹ, nhưng điều đó không xảy ra. Hãy xem phần Hợp nhất và xoá để biết thêm thông tin chi tiết.
Thành phần tuỳ chỉnh
Khi tạo một thành phần tuỳ chỉnh, hãy xem xét việc triển khai một thành phần tương tự trong thư viện Material hoặc các thư viện Compose khác. Sau đó, hãy bắt chước hoặc sửa đổi hành vi hỗ trợ tiếp cận của thành phần đó cho phù hợp. Ví dụ: nếu bạn thay thế Checkbox trong Material bằng phương pháp triển khai của riêng mình, thì việc xem xét phương pháp triển khai Checkbox hiện có sẽ nhắc bạn thêm đối tượng sửa đổi triStateToggleable. Đối tượng sửa đổi này sẽ xử lý các thuộc tính hỗ trợ tiếp cận cho thành phần. Ngoài ra, hãy tận dụng nhiều thành phần sửa đổi Foundation, vì các thành phần sửa đổi này bao gồm các tính năng hỗ trợ tiếp cận tích hợp và các phương pháp Compose hiện có được đề cập trong phần này.
Bạn cũng có thể tìm thấy ví dụ về thành phần nút bật/tắt tuỳ chỉnh trong phần Xoá và đặt ngữ nghĩa, cũng như thông tin chi tiết hơn về cách hỗ trợ khả năng tiếp cận trong các thành phần tuỳ chỉnh trong nguyên tắc về API.
Đề 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
- Kiểm thử bố cục Compose
