1. Giới thiệu
Trong lớp học lập trình này, bạn sẽ tìm hiểu cách sử dụng Jetpack Compose để cải thiện khả năng hỗ trợ tiếp cận của ứng dụng. Chúng ta sẽ thảo luận một số trường hợp sử dụng phổ biến và cải thiện ứng dụng mẫu theo từng bước. Chúng ta sẽ đề cập đến các chủ đề như kích thước đích nhấn, mô tả nội dung, nhãn lượt nhấp, v.v.
Người mắc suy giảm thị lực, mù màu, suy giảm thính lực, suy giảm độ khéo léo, khuyết tật trí tuệ và nhiều dạng khuyết tật khác sử dụng thiết bị Android để hoàn thành các công việc trong cuộc sống thường ngày của họ. Bằng việc phát triển ứng dụng có khả năng hỗ trợ tiếp cận, bạn có thể cải thiện trải nghiệm người dùng, đặc biệt là dành cho người dùng có các nhu cầu nói trên cũng như các nhu cầu hỗ trợ tiếp cận khác.
Trong lớp học lập trình này, chúng ta sử dụng TalkBack để kiểm thử các thay đổi về mã theo cách thủ công. TalkBack là một dịch vụ hỗ trợ tiếp cận chủ yếu được người khiếm thị sử dụng. Đừng quên kiểm thử mọi thay đổi mã với các dịch vụ hỗ trợ tiếp cận khác, chẳng hạn như Switch Access (Tiếp cận bằng công tắc).
TalkBack đang hoạt động trong ứng dụng Jetnews.
Kiến thức bạn sẽ học được
Trong lớp học lập trình này, bạn sẽ tìm hiểu:
- Cách phục vụ người dùng bị suy giảm độ khéo léo bằng cách tăng kích thước đích nhấn.
- Thuộc tính ngữ nghĩa là gì và cách để thay đổi loại thuộc tính này.
- Cách cung cấp thông tin để các thành phần kết hợp (composable) trở nên dễ tiếp cận hơn.
Bạn cần có
- Kinh nghiệm về cú pháp Kotlin, bao gồm cả lambda.
- Kinh nghiệm cơ bản về Compose Hãy cân nhắc việc tham gia lớp học lập trình cơ bản về Jetpack Compose trước khi tham gia lớp học lập trình này.
- Một thiết bị Android hoặc trình mô phỏng đã bật TalkBack.
Sản phẩm bạn sẽ tạo ra
Trong lớp học lập trình này, chúng ta sẽ cải thiện khả năng hỗ trợ tiếp cận của một ứng dụng đọc tin tức. Chúng ta sẽ bắt đầu bằng một ứng dụng chưa có các tính năng hỗ trợ tiếp cận thiết yếu, đồng thời áp dụng những kiến thức mà chúng ta học được để làm cho ứng dụng trở nên hữu ích hơn cho người có nhu cầu hỗ trợ tiếp cận.
2. Thiết lập
Trong bước này, bạn sẽ tải mã xuống cho lớp học lập trình này. Mã này bao gồm một ứng dụng đơn giản để đọc tin tức.
Bạn cần có
Lấy mã
Bạn có thể tìm thấy mã cho lớp học lập trình này trong kho lưu trữ codelab-android-compose trên GitHub. Để sao chép, hãy chạy:
$ git clone https://github.com/android/codelab-android-compose
Ngoài ra, bạn có thể tải 2 tệp zip xuống:
Xem xét ứng dụng mẫu
Mã bạn vừa tải xuống có chứa mã dành cho tất cả lớp học lập trình Compose hiện có. Để hoàn tất lớp học lập trình này, hãy mở dự án AccessibilityCodelab
trong Android Studio.
Bạn nên bắt đầu bằng mã trong nhánh main
và làm theo hướng dẫn từng bước của lớp học lập trình theo tiến độ phù hợp với bạn.
Thiết lập TalkBack
Trong lớp học lập trình này, chúng ta sẽ sử dụng TalkBack để kiểm tra các thay đổi. Khi bạn sử dụng thiết bị thực để kiểm thử, hãy làm theo hướng dẫn này để bật TalkBack. Theo mặc định, TalkBack không có sẵn trong các trình mô phỏng. Hãy chọn một trình mô phỏng chứa Cửa hàng Play rồi tải Android Accessibility Suite (Bộ hỗ trợ tiếp cận của Android) xuống.
3. Kích thước đích chạm
Bất kỳ thành phần nào trên màn hình mà một người có thể nhấp, chạm vào hoặc tương tác đều phải đủ lớn để đảm bảo độ tin cậy cho tương tác đó. Bạn nên đảm bảo các phần tử này có chiều rộng và chiều cao ít nhất là 48dp.
Nếu các đối tượng điều khiển này có kích thước động hoặc thay đổi kích thước dựa trên kích thước nội dung, thì bạn nên cân nhắc sử dụng đối tượng sửa đổi (modifier) sizeIn
để đặt ngưỡng dưới cho các kích thước.
Một số thành phần Material thay đổi những kích thước này cho bạn. Ví dụ: thành phần kết hợp Button (Nút) có MinHeight
được đặt thành 36dp và sử dụng khoảng đệm dọc 8dp. Giá trị này phải được tăng lên để đạt chiều cao cần thiết là 48dp.
Khi mở ứng dụng mẫu và chạy TalkBack, chúng ta nhận thấy biểu tượng chữ thập trong các thẻ tin bài có đích nhấn rất nhỏ. Chúng ta muốn đích nhấn này ít nhất phải là 48dp.
Đây là ảnh chụp màn hình ứng dụng gốc ở bên trái so với bản đã cải thiện ở bên phải.
Hãy xem quá trình triển khai và kiểm tra kích thước của thành phần kết hợp này. Mở PostCards.kt
rồi tìm thành phần kết hợp PostCardHistory
. Như bạn có thể thấy, quá trình triển khai sẽ đặt kích thước của biểu tượng trình đơn mục bổ sung thành 24dp:
@Composable
fun PostCardHistory(post: Post, navigateToArticle: (String) -> Unit) {
// ...
Row(
// ...
) {
// ...
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurfaceVariant) {
Icon(
imageVector = Icons.Default.Close,
contentDescription = stringResource(R.string.cd_show_fewer),
modifier = Modifier
.clickable { openDialog = true }
.size(24.dp)
)
}
}
// ...
}
Để tăng kích thước đích nhấn cho Icon
này, chúng ta có thể thêm khoảng đệm:
@Composable
fun PostCardHistory(post: Post, navigateToArticle: (String) -> Unit) {
// ...
Row(
// ...
) {
// ...
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurfaceVariant) {
Icon(
imageVector = Icons.Default.Close,
contentDescription = stringResource(R.string.cd_show_fewer),
modifier = Modifier
.clickable { openDialog = true }
.padding(12.dp)
.size(24.dp)
)
}
}
// ...
}
Trong trường hợp sử dụng của chúng ta, có một cách dễ dàng hơn để đảm bảo đích nhấn có kích thước ít nhất là 48 dp. Chúng ta có thể sử dụng thành phần Material IconButton
để xử lý vấn đề này:
@Composable
fun PostCardHistory(post: Post, navigateToArticle: (String) -> Unit) {
// ...
Row(
// ...
) {
// ...
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurfaceVariant) {
IconButton(onClick = { openDialog = true }) {
Icon(
imageVector = Icons.Default.Close,
contentDescription = stringResource(R.string.cd_show_fewer)
)
}
}
}
// ...
}
Giờ đây, khi bạn dùng màn hình TalkBack, TalkBack sẽ hiện chính xác vùng đích nhấn là 48 dp. Ngoài ra, IconButton
cũng thêm chỉ báo gợn sóng (ripple), cho người dùng biết rằng phần tử này nhấp vào được.
4. Nhãn lượt nhấp
Theo mặc định, thành phần nhấp vào được trong ứng dụng không đưa ra thông tin nào về điều sẽ xảy ra khi nhấp vào thành phần đó. Do đó, các dịch vụ hỗ trợ tiếp cận như TalkBack sẽ sử dụng một nội dung mô tả mặc định rất chung.
Để mang lại trải nghiệm tốt nhất cho người dùng có nhu cầu hỗ trợ tiếp cận, chúng ta có thể cung cấp nội dung mô tả cụ thể để giải thích điều sẽ xảy ra khi người dùng nhấp vào thành phần này.
Trong ứng dụng Jetnews, người dùng có thể nhấp vào các thẻ tin bài để đọc toàn bộ tin bài. Theo mặc định, thao tác này sẽ đọc to nội dung của phần tử nhấp vào được, tiếp theo là văn bản "Double tap to activate" ("Nhấn đúp để kích hoạt"). Thay vào đó, chúng ta muốn cụ thể hơn và sử dụng "Double tap to read article" ("Nhấn đúp để đọc bài viết"). Đây là giao diện của phiên bản gốc, so với giải pháp lý tưởng của chúng ta:
Thay đổi nhãn lượt nhấp (click label) của một thành phần kết hợp. Trước (ở bên trái) so với sau (ở bên phải).
Đối tượng sửa đổi clickable
bao gồm một tham số cho phép bạn trực tiếp thiết lập nhãn của lượt nhấp này.
Hãy cùng xem lại việc triển khai PostCardHistory
:
@Composable
fun PostCardHistory(
// ...
) {
Row(
Modifier.clickable { navigateToArticle(post.id) }
) {
// ...
}
}
Như bạn có thể thấy, quá trình triển khai này sử dụng đối tượng sửa đổi clickable
. Để đặt nhãn lượt nhấp, chúng ta có thể đặt giá trị cho tham số onClickLabel
:
@Composable
fun PostCardHistory(
// ...
) {
Row(
Modifier.clickable(
// R.string.action_read_article = "read article"
onClickLabel = stringResource(R.string.action_read_article)
) {
navigateToArticle(post.id)
}
) {
// ...
}
}
TalkBack hiện thông báo chính xác "Double tap to read article" ("Nhấn đúp để đọc bài viết").
Các thẻ tin bài khác trên màn hình chính có cùng nhãn lượt nhấp chung (generic click label). Hãy xem việc triển khai và cập nhật nhãn lượt nhấp của thành phần kết hợp PostCardPopular
:
@Composable
fun PostCardPopular(
// ...
) {
Card(
shape = MaterialTheme.shapes.medium,
modifier = modifier.size(280.dp, 240.dp),
onClick = { navigateToArticle(post.id) }
) {
// ...
}
}
Thành phần này có thể sử dụng nội bộ Card
, điều này không cho phép bạn đặt trực tiếp nhãn nhấp chuột. Thay vào đó, bạn có thể sử dụng đối tượng sửa đổi semantics
để đặt nhãn lượt nhấp:
@Composable
fun PostCardPopular(
post: Post,
navigateToArticle: (String) -> Unit,
modifier: Modifier = Modifier
) {
val readArticleLabel = stringResource(id = R.string.action_read_article)
Card(
shape = MaterialTheme.shapes.medium,
modifier = modifier
.size(280.dp, 240.dp)
.semantics { onClick(label = readArticleLabel, action = null) },
onClick = { navigateToArticle(post.id) }
) {
// ...
}
}
5. Thao tác tuỳ chỉnh
Nhiều ứng dụng cho thấy một số loại danh sách, trong đó mỗi mục trong danh sách lại chứa một hoặc nhiều thao tác. Khi sử dụng trình đọc màn hình, việc điều hướng một danh sách như vậy có thể trở nên tẻ nhạt, vì cùng một thao tác sẽ được lấy tâm điểm lặp đi lặp lại.
Thay vào đó, chúng ta có thể thêm các thao tác hỗ trợ tiếp cận tuỳ chỉnh vào một thành phần kết hợp. Bằng cách này, bạn có thể nhóm các thao tác liên quan đến cùng một mục danh sách lại với nhau.
Trong ứng dụng Jetnews, chúng ta đang hiện danh sách bài viết mà người dùng có thể đọc. Mỗi mục danh sách chứa một thao tác để cho biết người dùng muốn xem bớt nội dung về chủ đề này. Trong phần này, chúng ta sẽ di chuyển thao tác này sang một thao tác hỗ trợ tiếp cận tuỳ chỉnh, nhờ vậy, việc điều hướng trong danh sách sẽ dễ dàng hơn.
Ở bên trái, bạn có thể thấy tình huống mặc định, trong đó mỗi biểu tượng chữ thập đều có thể làm tâm điểm. Ở bên phải, bạn có thể thấy giải pháp, trong đó thao tác này được đưa vào các thao tác tuỳ chỉnh trong TalkBack:
Thêm thao tác tuỳ chỉnh vào một mục tin bài. Trước (ở bên trái) so với sau (ở bên phải).
Hãy mở PostCards.kt
và xem việc triển khai thành phần kết hợp PostCardHistory
. Hãy lưu ý các thuộc tính nhấp vào được của cả Row
và IconButton
bằng cách sử dụng Modifier.clickable
và onClick
:
@Composable
fun PostCardHistory(post: Post, navigateToArticle: (String) -> Unit) {
// ...
Row(
Modifier.clickable(
onClickLabel = stringResource(R.string.action_read_article)
) {
navigateToArticle(post.id)
}
) {
// ...
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurfaceVariant) {
IconButton(onClick = { openDialog = true }) {
Icon(
imageVector = Icons.Default.Close,
contentDescription = stringResource(R.string.cd_show_fewer)
)
}
}
}
// ...
}
Theo mặc định, cả thành phần kết hợp Row
và IconButton
đều có thể nhấp vào được. Vì vậy, TalkBack sẽ lấy tâm điểm vào đó. Việc này xảy ra cho từng mục trong danh sách, nghĩa là rất nhiều thao tác vuốt khi điều hướng trong danh sách. Chúng ta muốn đưa thao tác liên quan đến IconButton
vào thao tác tuỳ chỉnh trên mục danh sách. Chúng ta có thể yêu cầu Dịch vụ hỗ trợ tiếp cận không tương tác với Icon
này bằng cách sử dụng đối tượng sửa đổi clearAndSetSemantics
:
@Composable
fun PostCardHistory(post: Post, navigateToArticle: (String) -> Unit) {
// ...
Row(
Modifier.clickable(
onClickLabel = stringResource(R.string.action_read_article)
) {
navigateToArticle(post.id)
}
) {
// ...
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurfaceVariant) {
IconButton(
modifier = Modifier.clearAndSetSemantics { },
onClick = { openDialog = true }
) {
Icon(
imageVector = Icons.Default.Close,
contentDescription = stringResource(R.string.cd_show_fewer)
)
}
}
}
// ...
}
Tuy nhiên, nếu xoá ngữ nghĩa của IconButton
thì không còn cách để thực thi thao tác này nữa. Chúng ta có thể thêm thao tác này vào mục trong danh sách bằng cách thêm một thao tác tuỳ chỉnh trong đối tượng sửa đổi semantics
:
@Composable
fun PostCardHistory(post: Post, navigateToArticle: (String) -> Unit) {
// ...
val showFewerLabel = stringResource(R.string.cd_show_fewer)
Row(
Modifier
.clickable(
onClickLabel = stringResource(R.string.action_read_article)
) {
navigateToArticle(post.id)
}
.semantics {
customActions = listOf(
CustomAccessibilityAction(
label = showFewerLabel,
// action returns boolean to indicate success
action = { openDialog = true; true }
)
)
}
) {
// ...
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurfaceVariant) {
IconButton(
modifier = Modifier.clearAndSetSemantics { },
onClick = { openDialog = true }
) {
Icon(
imageVector = Icons.Default.Close,
contentDescription = showFewerLabel
)
}
}
}
// ...
}
Bây giờ, chúng ta có thể dùng cửa sổ thao tác tuỳ chỉnh bật lên trong TalkBack để áp dụng thao tác đó. Việc này ngày càng cần đến hơn khi số lượng thao tác bên trong một mục danh sách tăng lên.
6. Mô tả phần tử hình ảnh
Không phải người dùng ứng dụng nào cũng có thể xem hoặc diễn giải thành phần hình ảnh xuất hiện trong ứng dụng, chẳng hạn như biểu tượng và hình minh hoạ. Ngoài ra, không có cách nào để các dịch vụ hỗ trợ tiếp cận hiểu được các yếu tố hình ảnh nếu chỉ dựa trên pixel của hình ảnh. Là nhà phát triển, bạn cần cung cấp thêm thông tin về các thành phần hình ảnh trong ứng dụng cho các dịch vụ hỗ trợ tiếp cận.
Các thành phần kết hợp dạng hình ảnh như Image
và Icon
chứa một tham số contentDescription
. Trong tham số này, bạn truyền thông tin mô tả đã bản địa hoá cho thành phần hình ảnh đó hoặc null
nếu thành phần hình ảnh chỉ mang tính chất trang trí.
Trong ứng dụng của chúng ta, màn hình bài viết thiếu một số thông tin mô tả nội dung. Hãy chạy ứng dụng rồi chọn bài viết trên cùng để điều hướng đến màn hình bài viết.
Thêm thông tin mô tả cho nội dung hình ảnh. Trước (ở bên trái) so với sau (ở bên phải).
Khi chúng ta không đưa ra thông tin nào, biểu tượng điều hướng trên cùng bên trái sẽ chỉ thông báo "Button, double tap to activate" ("Nút — nhấn đúp để kích hoạt"). Thông báo này không cho người dùng biết thông tin gì về thao tác sẽ được thực hiện khi họ kích hoạt nút đó. Hãy mở ArticleScreen.kt
:
@Composable
fun ArticleScreen(
// ...
) {
// ...
Scaffold(
topBar = {
InsetAwareTopAppBar(
title = {
// ...
},
navigationIcon = {
IconButton(onClick = onBack) {
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = null
)
}
}
)
}
) {
// ...
}
}
Thêm thông tin mô tả nội dung có ý nghĩa cho Icon (Biểu tượng):
@Composable
fun ArticleScreen(
// ...
) {
// ...
Scaffold(
topBar = {
InsetAwareTopAppBar(
title = {
// ...
},
navigationIcon = {
IconButton(onClick = onBack) {
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = stringResource(
R.string.cd_navigate_up
)
)
}
}
)
}
) {
// ...
}
}
Một phần tử hình ảnh khác trong bài viết này là hình ảnh tiêu đề. Trong trường hợp này, hình ảnh này chỉ mang tính chất trang trí và không biểu thị nội dung nào mà chúng ta cần truyền tải cho người dùng. Do đó, thông tin mô tả nội dung được đặt thành null
và phần tử này bị bỏ qua khi chúng ta sử dụng dịch vụ hỗ trợ tiếp cận.
Phần tử hình ảnh cuối cùng trong màn hình của chúng ta là ảnh hồ sơ. Trong trường hợp này, chúng ta sử dụng một hình đại diện chung, nên bạn không cần thêm thông tin mô tả nội dung ở đây. Khi sử dụng ảnh hồ sơ thực tế của tác giả này, chúng ta có thể yêu cầu tác giả cung cấp thông tin mô tả nội dung phù hợp cho ảnh đó.
7. Tiêu đề
Khi một màn hình chứa nhiều văn bản (chẳng hạn như màn hình bài viết), người dùng gặp khó khăn về thị giác rất khó tìm nhanh được phần mà họ cần. Để trợ giúp họ, chúng ta có thể cho biết phần văn bản nào là tiêu đề. Sau đó, người dùng có thể nhanh chóng điều hướng trên các tiêu đề bằng cách vuốt lên hoặc xuống.
Theo mặc định, không thành phần kết hợp nào được đánh dấu là tiêu đề, vì vậy sẽ không thể điều hướng được. Chúng ta muốn màn hình bài viết của chúng ta cung cấp tính năng điều hướng theo từng tiêu đề một:
Thêm tiêu đề. Trước (ở bên trái) so với sau (ở bên phải).
Các tiêu đề trong bài viết của chúng ta được khai báo trong PostContent.kt
. Hãy mở tệp đó rồi cuộn đến thành phần kết hợp Paragraph
:
@Composable
private fun Paragraph(paragraph: Paragraph) {
// ...
Box(modifier = Modifier.padding(bottom = trailingPadding)) {
when (paragraph.type) {
// ...
ParagraphType.Header -> {
Text(
modifier = Modifier.padding(4.dp),
text = annotatedString,
style = textStyle.merge(paragraphStyle)
)
}
// ...
}
}
}
Ở đây, Header
được khai báo là một thành phần kết hợp Text
đơn giản. Chúng ta có thể đặt thuộc tính ngữ nghĩa heading
để cho biết thành phần kết hợp này là một tiêu đề.
@Composable
private fun Paragraph(paragraph: Paragraph) {
// ...
Box(modifier = Modifier.padding(bottom = trailingPadding)) {
when (paragraph.type) {
// ...
ParagraphType.Header -> {
Text(
modifier = Modifier.padding(4.dp)
.semantics { heading() },
text = annotatedString,
style = textStyle.merge(paragraphStyle)
)
}
// ...
}
}
}
8. Hợp nhất tuỳ chỉnh
Như chúng ta đã thấy trong các bước trước, các dịch vụ hỗ trợ tiếp cận như TalkBack điều hướng trên màn hình theo từng phần tử một. Theo mặc định, mỗi thành phần kết hợp cấp độ thấp trong Jetpack Compose lại đặt ít nhất một thuộc tính ngữ nghĩa sẽ nhận được tâm điểm. Ví dụ: một thành phần kết hợp Text
đặt thuộc tính ngữ nghĩa text
và do đó nhận được tâm điểm.
Tuy nhiên, việc có quá nhiều phần tử có thể làm tâm điểm lại có thể gây nhầm lẫn khi người dùng điều hướng theo từng phần tử một. Thay vào đó, bạn có thể hợp nhất các thành phần kết hợp với nhau bằng đối tượng sửa đổi semantics
chứa thuộc tính mergeDescendants
tương ứng.
Hãy kiểm tra màn hình bài viết. Hầu hết phần tử đều có được cấp độ tâm điểm phù hợp. Tuy nhiên, siêu dữ liệu của bài viết hiện được đọc to thành nhiều mục riêng biệt. Bạn có thể cải thiện việc này bằng cách hợp nhất các phần tử đó thành một thực thể có thể làm tâm điểm:
Hợp nhất các thành phần kết hợp. Trước (ở bên trái) so với sau (ở bên phải).
Hãy mở PostContent.kt
và kiểm tra thành phần kết hợp PostMetadata
:
@Composable
private fun PostMetadata(metadata: Metadata) {
// ...
Row {
Image(
// ...
)
Spacer(Modifier.width(8.dp))
Column {
Text(
// ...
)
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurfaceVariant) {
Text(
// ..
)
}
}
}
}
Chúng ta có thể yêu cầu hàng cấp cao nhất hợp nhất các thành phần con. Việc này sẽ dẫn đến hành vi mà chúng ta muốn:
@Composable
private fun PostMetadata(metadata: Metadata) {
// ...
Row(Modifier.semantics(mergeDescendants = true) {}) {
Image(
// ...
)
Spacer(Modifier.width(8.dp))
Column {
Text(
// ...
)
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurfaceVariant) {
Text(
// ..
)
}
}
}
}
9. Nút chuyển và hộp đánh dấu
Các thành phần có thể bật/tắt như Switch
và Checkbox
sẽ đọc to trạng thái đã đánh dấu khi được TalkBack chọn. Nếu không có ngữ cảnh thì có thể sẽ khó mà hiểu được ý nghĩa của những thành phần có thể bật/tắt này. Chúng ta có thể thêm ngữ cảnh cho thành phần có thể bật/tắt bằng cách nâng trạng thái có thể bật/tắt lên để người dùng có thể bật/tắt Switch
hoặc Checkbox
bằng cách nhấn vào chính thành phần kết hợp đó hoặc nhấn vào nhãn mô tả thành phần kết hợp đó.
Chúng ta có thể xem ví dụ về việc này trong màn hình Interests (Mối quan tâm). Bạn có thể điều hướng đến đó bằng cách mở ngăn điều hướng trên Màn hình chính. Trên màn hình Interests (Mối quan tâm), chúng ta có danh sách chủ đề mà người dùng có thể đăng ký. Theo mặc định, các hộp đánh dấu trên màn hình này được lấy tiêu điểm mà không có liên hệ với nhãn. Điều này khiến người dùng khó mà hiểu được ngữ cảnh của hộp đánh dấu. Chúng ta muốn toàn bộ Row
đều bật/tắt được:
Xử lý hộp đánh dấu. Trước (ở bên trái) so với sau (ở bên phải).
Hãy mở InterestsScreen.kt
và xem cách triển khai thành phần kết hợp TopicItem
:
@Composable
private fun TopicItem(itemTitle: String, selected: Boolean, onToggle: () -> Unit) {
// ...
Row(
modifier = Modifier
.padding(horizontal = 16.dp, vertical = 8.dp)
) {
// ...
Checkbox(
checked = selected,
onCheckedChange = { onToggle() },
modifier = Modifier.align(Alignment.CenterVertically)
)
}
}
Như bạn có thể thấy ở đây, Checkbox
có lệnh gọi lại onCheckedChange
xử lý việc bật/tắt phần tử. Chúng ta có thể nâng lệnh gọi lại này lên toàn bộ Row
:
@Composable
private fun TopicItem(itemTitle: String, selected: Boolean, onToggle: () -> Unit) {
// ...
Row(
modifier = Modifier
.toggleable(
value = selected,
onValueChange = { _ -> onToggle() },
role = Role.Checkbox
)
.padding(horizontal = 16.dp, vertical = 8.dp)
) {
// ...
Checkbox(
checked = selected,
onCheckedChange = null,
modifier = Modifier.align(Alignment.CenterVertically)
)
}
}
10. Mô tả trạng thái
Trong bước trước, chúng ta đã nâng hành vi bật/tắt từ lớp con Checkbox
lên lớp mẹ Row
. Chúng ta có thể cải thiện khả năng hỗ trợ tiếp cận của thành phần này hơn nữa bằng cách thêm thông tin mô tả tuỳ chỉnh cho trạng thái của thành phần kết hợp.
Theo mặc định, trạng thái Checkbox
sẽ được đọc là "Ticked" ("Đã đánh dấu") hoặc "Not ticked" ("Chưa đánh dấu"). Chúng ta có thể thay thế thông tin mô tả này bằng thông tin mô tả tuỳ chỉnh của riêng mình:
Thêm nội dung mô tả trạng thái Trước (ở bên trái) so với sau (ở bên phải).
Chúng ta có thể tiếp tục với thành phần kết hợp TopicItem
mà chúng ta đã điều chỉnh ở bước cuối cùng:
@Composable
private fun TopicItem(itemTitle: String, selected: Boolean, onToggle: () -> Unit) {
// ...
Row(
modifier = Modifier
.toggleable(
value = selected,
onValueChange = { _ -> onToggle() },
role = Role.Checkbox
)
.padding(horizontal = 16.dp, vertical = 8.dp)
) {
// ...
Checkbox(
checked = selected,
onCheckedChange = null,
modifier = Modifier.align(Alignment.CenterVertically)
)
}
}
Chúng ta có thể thêm nội dung mô tả trạng thái tuỳ chỉnh bằng cách sử dụng thuộc tính stateDescription
bên trong đối tượng sửa đổi semantics
:
@Composable
private fun TopicItem(itemTitle: String, selected: Boolean, onToggle: () -> Unit) {
// ...
val stateNotSubscribed = stringResource(R.string.state_not_subscribed)
val stateSubscribed = stringResource(R.string.state_subscribed)
Row(
modifier = Modifier
.semantics {
stateDescription = if (selected) {
stateSubscribed
} else {
stateNotSubscribed
}
}
.toggleable(
value = selected,
onValueChange = { _ -> onToggle() },
role = Role.Checkbox
)
.padding(horizontal = 16.dp, vertical = 8.dp)
) {
// ...
Checkbox(
checked = selected,
onCheckedChange = null,
modifier = Modifier.align(Alignment.CenterVertically)
)
}
}
11. Xin chúc mừng!
Xin chúc mừng, bạn đã hoàn tất thành công lớp học lập trình này và tìm hiểu thêm về tính năng hỗ trợ tiếp cận trong Compose. Bạn đã tìm hiểu về đích nhấn, thông tin mô tả phần tử hình ảnh và thông tin mô tả trạng thái. Bạn đã thêm nhãn lượt nhấp, tiêu đề và thao tác tuỳ chỉnh. Bạn đã nắm được cách thêm yếu tố hợp nhất tuỳ chỉnh cũng như cách xử lý nút chuyển và hộp đánh dấu. Việc áp dụng những kiến thức này vào ứng dụng sẽ giúp ứng dụng cải thiện đáng kể khả năng hỗ trợ tiếp cận!
Hãy tham khảo các lớp học lập trình khác trên Lộ trình học tập về Compose. Cũng như các mã mẫu khác, bao gồm cả Jetnews.
Tài liệu
Để biết thêm thông tin và hướng dẫn về những chủ đề này, hãy xem các tài liệu sau: