Để giúp những người có nhu cầu hỗ trợ tiếp cận sử dụng thành công ứng dụng của bạn, hãy thiết kế để hỗ trợ các yêu cầu chính về hỗ trợ tiếp cận.
Xem xét kích thước đích chạm tối thiểu
Mọi phần tử trên màn hình mà ai đó có thể nhấp, chạm vào hoặc tương tác đều phải đủ lớn để tương tác đáng tin cậy. Khi đị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 thành 48dp để tuân thủ chính xác Material Design nguyên tắc hỗ trợ tiếp cận.
Thành phần Material – như Checkbox
, RadioButton
, Switch
,
Slider
và Surface
– đặt kích thước tối thiểu này trong nội bộ, nhưng chỉ
khi thành phần đó có thể nhận thao tác của người dùng. Ví dụ: khi Checkbox
có
tham số onCheckedChange
đượ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ối thiểu 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
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 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ẹ, đặt
lệnh gọi lại nhấp chuột trên thành phần kết hợp vào null
rồi thêm toggleable
hoặc
Đối tượng sửa đổi 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. Để làm được điều đó, công cụ này mở rộng kích thước đích chạm nằm ngoài ranh giới của thành phần kết hợp.
Ví dụ sau chứa một Box
rất nhỏ có thể nhấp vào. Đích chạm
diện tích đó sẽ tự động mở rộng ra ngoài phạm vi của Box
, vì vậy nhấn
bên cạnh Box
vẫn kích hoạt sự kiện nhấp chuột.
@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 sự chồng chéo có thể có giữa các khu vực cảm ứng của các thành phần kết hợp khác nhau, hãy luôn 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ụ, điều đó sẽ
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) ) } }
Thêm nhãn nhấp chuột
Bạn có thể sử dụng nhãn nhấp chuột để thêm ý nghĩa ngữ nghĩa vào hành vi nhấp của một thành phần có thể kết hợp. Nhãn lượt nhấp mô tả những gì sẽ xảy ra khi người dùng tương tác với thành phần kết hợp. Các dịch vụ hỗ trợ tiếp cận sử dụng nhãn lượt nhấp để giúp mô tả ứng dụng người dùng có nhu cầu cụ thể.
Đặt nhãn lượt nhấp bằng cách truyền một tham số vào đối tượng sửa đổi clickable
:
@Composable private fun ArticleListItem(openArticle: () -> Unit) { Row( Modifier.clickable( // R.string.action_read_article = "read article" onClickLabel = stringResource(R.string.action_read_article), onClick = openArticle ) ) { // .. } }
Ngoài ra, nếu bạn không có quyền truy cập vào đối tượng sửa đổi có thể nhấp, hãy đặt nhãn nhấp chuột trong đối tượng sửa đổi ngữ nghĩa:
@Composable private fun LowLevelClickLabel(openArticle: () -> Boolean) { // R.string.action_read_article = "read article" val readArticleLabel = stringResource(R.string.action_read_article) Canvas( Modifier.semantics { onClick(label = readArticleLabel, action = openArticle) } ) { // .. } }
Mô tả phần tử hình ảnh
Khi bạn xác định thành phần kết hợp Image
hoặc Icon
, sẽ không có
tự động để khung Android hiểu được ứng dụng là gì
đang hiển thị. Bạn cần truyền nội dung mô tả dạng văn bản của phần tử hình ảnh.
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 không thể mô tả nó bằng một hình ảnh trực quan người dùng khiếm khuyết. Khung Android cần có thêm mô tả bằng văn bản về biểu tượng.
Tham số contentDescription
mô tả một phần tử hình ảnh. Hãy sử dụng
vì URL hiển thị với người dùng.
@Composable private fun ShareButton(onClick: () -> Unit) { IconButton(onClick = onClick) { Icon( imageVector = Icons.Filled.Share, contentDescription = stringResource(R.string.label_share) ) } }
Một số thành phần hình ảnh chỉ mang tính chất trang trí và có thể bạn sẽ không muốn truyền đạt
cho người dùng. Khi đặt tham số contentDescription
thành null
, bạn
cho khung Android biết rằng phần tử này không được liên kết
hành động hoặc trạng thái.
@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) ) }
Bạn có toàn quyền quyết định xem một phần tử hình ảnh nhất định có cần có contentDescription
hay không. Hãy tự hỏi liệu các phần tử đó có truyền tải thông tin mà
người dùng cần thực hiện tác vụ của họ. Nếu không, bạn nên để
nội dung mô tả.
Hợp nhất phần tử
Các dịch vụ Hỗ trợ tiếp cận như TalkBack và Tiếp cận bằng công tắc cho phép người dùng di chuyển trọng tâm đến các phần tử trên màn hình. Điều quan trọng là các phần tử phải được tập trung với độ chi tiết phù hợp. Khi mọi thành phần kết hợp cấp thấp trong màn hình của bạn được tập trung độc lập, nên người dùng 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á nhiều, người dùng có thể không hiểu các thành phần thuộc về nhau
Khi bạn áp dụng một đối tượng sửa đổi clickable
cho một thành phần kết hợp, Compose
tự động hợp nhất tất cả các phần tử có trong thành phần kết hợp đó. Điều này cũng đúng với
ListItem
; các phần tử trong một mục danh sách hợp nhất với nhau và khả năng hỗ trợ tiếp cận
xem chúng dưới dạng một phần tử.
Có thể có một tập hợp các thành phần có thể kết hợp tạo thành một nhóm logic, nhưng nhóm đó không thể nhấp vào được hoặc thuộc một mục danh sách. Bạn vẫn muốn hỗ trợ tiếp cận để xem chúng dưới dạng một phần tử. Ví dụ: hãy tưởng tượng một thành phần kết hợp hiển thị hình đại diện, tên của người dùng và một số thông tin bổ sung:
Bạn có thể cho phép Compose hợp nhất các phần tử này bằng cách sử dụng mergeDescendants
trong đối tượng sửa đổi semantics
. Bằng cách này, các dịch vụ hỗ trợ tiếp cận
chỉ chọn phần tử đã hợp nhất và tất cả các thuộc tính ngữ nghĩa của các phần tử con
đượ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") } } }
Dịch vụ hỗ trợ tiếp cận hiện tập trung vào toàn bộ vùng chứa cùng một lúc, hợp nhất nội dung của chúng:
Thêm thao tác tuỳ chỉnh
Hãy xem mục danh sách sau đây:
Khi bạn sử dụng một trình đọc màn hình như TalkBack để nghe nội dung hiển thị trên trên màn hình, trước tiên sẽ chọn toàn bộ mục, sau đó chọn biểu tượng dấu trang.
Trong một danh sách dài, điều này có thể lặp lại rất nhiều lần. Một phương pháp hiệu quả hơn là
xác định một thao tác tuỳ chỉnh cho phép người dùng đánh dấu mục đó. Lưu ý
bạn cũng phải xoá rõ ràng hành vi của biểu tượng dấu trang
để đảm bảo dịch vụ hỗ trợ tiếp cận không chọn nội dung đó. Chiến dịch này
được thực hiện bằng đối tượng sửa đổi clearAndSetSemantics
:
@Composable private fun PostCardSimple( /* ... */ isFavorite: Boolean, onToggleFavorite: () -> Boolean ) { val actionLabel = stringResource( if (isFavorite) R.string.unfavorite else R.string.favorite ) Row( modifier = Modifier .clickable(onClick = { /* ... */ }) .semantics { // Set any explicit semantic properties customActions = listOf( CustomAccessibilityAction(actionLabel, onToggleFavorite) ) } ) { /* ... */ BookmarkButton( isBookmarked = isFavorite, onClick = onToggleFavorite, // Clear any semantics properties set on this node modifier = Modifier.clearAndSetSemantics { } ) } }
Mô tả trạng thái của một nguyên tố
Một thành phần kết hợp có thể xác định stateDescription
cho ngữ nghĩa mà
Khung Android dùng để đọc trạng thái của thành phần kết hợp. Cho
Ví dụ: một thành phần kết hợp bật/tắt được có thể ở dạng "đã đánh dấu" hoặc "đã bỏ chọn"
trạng thái. Trong một số trường hợp, bạn có thể muốn ghi đè nội dung mô tả trạng thái mặc định
mà Compose sử dụng. Bạn có thể thực hiện điều này bằng cách chỉ định rõ trạng thái
nhãn mô tả trước khi xác định một thành phần kết hợp có thể bật/tắt:
@Composable private fun TopicItem(itemTitle: String, selected: Boolean, onToggle: () -> Unit) { val stateSubscribed = stringResource(R.string.subscribed) val stateNotSubscribed = stringResource(R.string.not_subscribed) Row( modifier = Modifier .semantics { // Set any explicit semantic properties stateDescription = if (selected) stateSubscribed else stateNotSubscribed } .toggleable( value = selected, onValueChange = { onToggle() } ) ) { /* ... */ } }
Xác định tiêu đề
Đôi khi, ứng dụng hiển thị nhiều nội dung trên một màn hình trong một vùng chứa có thể cuộn. Ví dụ: một màn hình có thể hiển thị toàn bộ nội dung của một bài viết mà người dùng đang đọc:
Người dùng có nhu cầu hỗ trợ tiếp cận sẽ gặp khó khăn khi di chuyển trên màn hình như vậy. Để hỗ trợ điều hướng, cho biết phần tử nào là tiêu đề. Trong ví dụ trước, mỗi tiêu đề tiểu mục có thể được xác định là tiêu đề cho khả năng tiếp cận. Hơi nhiều các dịch vụ hỗ trợ tiếp cận, như TalkBack, cho phép người dùng điều hướng trực tiếp từ đến tiêu đề.
Trong Compose, bạn cho biết thành phần kết hợp là tiêu đề bằng cách xác định
Thuộc tính semantics
:
@Composable private fun Subsection(text: String) { Text( text = text, style = MaterialTheme.typography.headlineSmall, modifier = Modifier.semantics { heading() } ) }
Xử lý thành phần kết hợp tuỳ chỉnh
Bất cứ khi nào bạn thay thế một số thành phần Material trong ứng dụng bằng thành phần tuỳ chỉnh phiên bản, bạn phải lưu ý đến việc cân nhắc về khả năng hỗ trợ tiếp cận.
Giả sử bạn đang thay thế Material Checkbox
bằng phương thức triển khai của riêng mình.
Bạn có thể quên thêm đối tượng sửa đổi triStateToggleable
để xử lý
các thuộc tính hỗ trợ tiếp cận cho thành phần này.
Theo quy tắc chung, hãy xem xét việc triển khai thành phần này trong thư viện Material và bắt chước mọi hành vi hỗ trợ tiếp cận mà bạn có thể tìm thấy. Ngoài ra, hãy tận dụng nhiều thành phần sửa đổi Foundation, chứ không phải các thành sửa đổi ở cấp độ giao diện người dùng, 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 ngay từ đầu.
Kiểm thử việc triển khai thành phần tuỳ chỉnh bằng nhiều dịch vụ hỗ trợ tiếp cận để xác minh hành vi của dịch vụ đó.
Tài nguyên khác
- Hỗ trợ tiếp cận: Các khái niệm và khái niệm cơ bản những kỹ thuật phổ biến đối với mọi hoạt động phát triển ứng dụng Android
- Xây dựng ứng dụng dễ tiếp cận: Các bước quan trọng bạn có thể thực hiện để tăng khả năng hỗ trợ tiếp cận cho ứng dụng
- Các nguyên tắc giúp cải thiện ứng dụng hỗ trợ tiếp cận: Các nguyên tắc chính để cần lưu ý khi tìm cách làm cho ứng dụng dễ tiếp cận hơn
- Kiểm thử khả năng hỗ trợ tiếp cận: Kiểm thử các nguyên tắc và công cụ hỗ trợ tiếp cận của Android