Để hỗ trợ người dùng có nhu cầu về hỗ trợ tiếp cận, khung Android cho phép bạn tạo dịch vụ hỗ trợ tiếp cận có thể trình bày nội dung của ứng dụng cho người dùng, đồng thời cũng vận hành ứng dụng thay cho người dùng.
Android cung cấp một số dịch vụ hỗ trợ tiếp cận hệ thống, bao gồm:
- TalkBack: giúp người có thị lực kém hoặc người khiếm thị. Dịch vụ này thông báo nội dung thông qua một giọng nói tổng hợp và thực hiện các thao tác trên ứng dụng theo cử chỉ của người dùng.
- Tiếp cận bằng công tắc: giúp người bị khuyết tật vận động. Dịch vụ này làm nổi bật các thành phần tương tác và thực hiện các thao tác để phản hồi khi người dùng nhấn nút. Dịch vụ này cũng cho phép điều khiển thiết bị chỉ bằng một hoặc hai nút.
Để giúp người có nhu cầu hỗ trợ tiếp cận sử dụng được ứng dụng của bạn, ứng dụng phải làm theo các phương pháp hay nhất được mô tả trên trang này. Các phương pháp này được xây dựng dựa trên các nguyên tắc được mô tả trong phần Giúp ứng dụng dễ tiếp cận hơn.
Mỗi phương pháp hay nhất sau đây (mô tả trong các phần tiếp theo) có thể cải thiện khả năng hỗ trợ tiếp cận của ứng dụng:
- Thành phần nhãn
- Người dùng phải hiểu được nội dung và mục đích của từng thành phần tương tác và giao diện người dùng có ý nghĩa trong ứng dụng.
- Thêm thao tác hỗ trợ tiếp cận
- Bằng cách thêm hành động hỗ trợ tiếp cận, bạn đã cung cấp cho người dùng các dịch vụ hỗ trợ tiếp cận để hoàn thành các luồng người dùng quan trọng trong ứng dụng của bạn.
- Sử dụng bộ tính năng hỗ trợ tiếp cận tích hợp sẵn
- Theo mặc định, Compose cung cấp nhiều hành vi hỗ trợ tiếp cận. Tận dụng các hành vi hỗ trợ tiếp cận được xác định trước để giúp các thành phần của bạn dễ tiếp cận mà không cần làm thêm gì hoặc chỉ cần làm thêm một chút. Compose cũng cung cấp các cách để hỗ trợ những yêu cầu cụ thể hơn về hỗ trợ tiếp cận mà các tính năng mặc định không đáp ứng được.
- Dùng chú thích ngoài màu sắc
- Người dùng phải phân biệt được rõ ràng các danh mục thành phần trong giao diện người dùng. Để làm như vậy, hãy sử dụng các mẫu và vị trí để thể hiện những khác biệt này (cùng với màu sắc).
- Tăng khả năng hỗ trợ tiếp cận của nội dung đa phương tiện
- Hãy thêm nội dung mô tả vào nội dung video hoặc âm thanh của ứng dụng để người dùng tiêu thụ nội dung này không cần dựa vào các gợi ý hoàn toàn bằng hình ảnh hay âm thanh.
Thành phần nhãn
Quan trọng là bạn phải cung cấp cho người dùng các nhãn hữu ích và mang tính mô tả cho từng phần tử trên giao diện người dùng tương tác trong ứng dụng. Mỗi nhãn phải giải thích ngữ nghĩa của một phần tử cụ thể, tức là ý nghĩa và mục đích của phần tử đó. Các trình đọc màn hình như TalkBack có thể thông báo các nhãn này cho người dùng.
Trong hầu hết các trường hợp, API Compose và Material đều có sẵn tính năng hỗ trợ tiếp cận mặc định. Tuy nhiên, nếu bạn cần chỉ định thủ công các thuộc tính ngữ nghĩa của một phần tử trên giao diện người dùng, hãy sử dụng đối tượng sửa đổi semantics và thuộc tính contentDescription. Để biết thêm thông tin về ngữ nghĩa, hãy xem phần Ngữ nghĩa.
Các phần sau đây mô tả một số kỹ thuật gắn nhãn khác.
Thành phần có thể chỉnh sửa
Khi gắn nhãn thành phần có thể chỉnh sửa (chẳng hạn như trường văn bản), bạn nên cho thấy văn bản đưa ra ví dụ về giá trị nhập hợp lệ trong chính thành phần đó, bên cạnh việc tạo văn bản mẫu được cung cấp cho trình đọc màn hình. Trong những trường hợp này, bạn có thể sử dụng văn bản phần giữ chỗ, còn gọi là văn bản gợi ý.
Trong ví dụ sau, TextField có tham số placeholder cung cấp văn bản gợi ý.
val usernameState = rememberTextFieldState() TextField( state = usernameState, lineLimits = TextFieldLineLimits.SingleLine, placeholder = { Text("Enter Username") } )
Thông thường, trường văn bản cũng có một nhãn mô tả tương ứng để mô tả nội dung mà người dùng phải nhập.
Trong ví dụ sau, TextField có tham số label cung cấp nội dung mô tả về khả năng hỗ trợ tiếp cận.
TextField( state = rememberTextFieldState(initialText = "Hello"), label = { Text("Label") } )
Để biết thêm thông tin về văn bản và hoạt động đầu vào của người dùng, hãy xem bài viết Định cấu hình trường văn bản.
Các thành phần trong một bộ sưu tập
Khi thêm nhãn vào các thành phần của một bộ sưu tập, mỗi nhãn phải là duy nhất. Nhờ vậy, các dịch vụ hỗ trợ tiếp cận của hệ thống có thể tham chiếu chính xác một thành phần trên màn hình khi bạn thông báo về một nhãn. Quan hệ tương ứng này giúp người dùng biết được thời điểm họ đi qua giao diện người dùng hoặc khi họ di chuyển tiêu điểm đến một thành phần mà họ phát hiện ra.
Ví dụ: khi bạn có LazyColumn hoặc LazyRow, hãy sử dụng đối tượng sửa đổi semantics để chỉ định một collectionItemInfo duy nhất cho mỗi mục, như trong đoạn mã sau:
MilkyWayList( modifier = Modifier .semantics { collectionInfo = CollectionInfo( rowCount = milkyWay.count(), columnCount = 1 ) } ) { milkyWay.forEachIndexed { index, text -> Text( text = text, modifier = Modifier.semantics { collectionItemInfo = CollectionItemInfo(index, 0, 0, 0) } ) } }
Để biết thêm thông tin về các thuộc tính ngữ nghĩa cho danh sách và lưới, hãy xem phần Thông tin về danh sách và mục.
Nhóm nội dung có liên quan
Nếu ứng dụng của bạn hiện một số phần tử trên giao diện người dùng để tạo thành một nhóm tự nhiên, chẳng hạn như thông tin chi tiết về một bài hát hoặc thuộc tính của một tin nhắn, hãy sắp xếp các phần tử đó trong một vùng chứa mẹ (như Column, Row hoặc Box). Sử dụng đối tượng sửa đổi semantics của vùng chứa mẹ để đặt mergeDescendants thành true.
Nhờ vậy, các dịch vụ hỗ trợ tiếp cận có thể trình bày từng phần mô tả nội dung của các thành phần bên trong một thông báo. Việc hợp nhất các thành phần có liên quan giúp người dùng công nghệ hỗ trợ khám phá thông tin trên màn hình một cách hiệu quả hơn.
Trong đoạn mã sau, thành phần kết hợp Row đóng vai trò là vùng chứa mẹ.
Trong Row là các phần tử liên quan cho thấy siêu dữ liệu của một bài đăng trên blog – hình đại diện của tác giả, tên tác giả và thời gian đọc ước tính.
Việc đặt mergeDescendants thành true sẽ nhóm các phần tử bên trong này, nhờ đó, các dịch vụ hỗ trợ tiếp cận có thể coi các phần tử này là một đơn vị.
@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") } } }
Khi nhóm các phần tử có liên quan như trong ví dụ trước, chỉ đặt vùng chứa mẹ ở chế độ tương tác. Tránh thêm đối tượng sửa đổi clickable hoặc focusable vào các phần tử con bên trong. Thay vào đó, hãy áp dụng đối tượng sửa đổi cho Row hoặc Column mẹ.
Vì các dịch vụ hỗ trợ tiếp cận thông báo về nội dung mô tả của thành phần bên trong qua một cách nói duy nhất, nên bạn cần phải giữ cho mỗi đoạn mô tả càng ngắn gọn càng tốt, đồng thời vẫn truyền tải ý nghĩa của các thành phần đó.
Lưu ý: Nhìn chung, khi tạo nội dung mô tả cho một nhóm, hãy tránh tổng hợp văn bản của các thành phần con. Làm như vậy sẽ khiến nội dung mô tả về nhóm đó trở nên rườm rà; cũng như nếu văn bản của thành phần con thay đổi thì có thể nội dung mô tả về nhóm đó sẽ không khớp với văn bản mà người dùng thấy.
Đối với danh sách hoặc lưới, trình đọc màn hình có thể hợp nhất văn bản trên các nút văn bản con của phần tử lưới hoặc danh sách. Bạn không nên sửa đổi thông báo này.
Để biết thêm thông tin về cách hợp nhất ngữ nghĩa, hãy xem phần Hợp nhất và xoá.
Tiêu đề trong văn bản
Một số ứng dụng dùng tiêu đề (heading) để tóm tắt các nhóm văn bản xuất hiện trên màn hình. Nếu một thành phần cụ thể đại diện cho tiêu đề, bạn có thể cho biết mục đích của thành phần đó cho các dịch vụ hỗ trợ tiếp cận bằng cách đặt thuộc tính heading trong đối tượng sửa đổi semantics.
@Composable private fun Subsection(text: String) { Text( text = text, style = MaterialTheme.typography.headlineSmall, modifier = Modifier.semantics { heading() } ) }
Người dùng các dịch vụ hỗ trợ tiếp cận có thể chọn di chuyển giữa các tiêu đề thay vì giữa các đoạn văn hoặc giữa các từ. Tính linh hoạt này cải thiện trải nghiệm điều hướng bằng văn bản.
Để biết thêm thông tin về thuộc tính ngữ nghĩa heading, hãy xem phần Tiêu đề.
Tiêu đề của ngăn hỗ trợ tiếp cận
Trong Android 9 (API cấp 28) trở lên, bạn có thể cung cấp tiêu đề dễ hỗ trợ tiếp cận cho các ngăn (pane) trên màn hình. Đối với mục đích hỗ trợ tiếp cận, ngăn là một phần riêng biệt của cửa sổ.
Để các dịch vụ hỗ trợ tiếp cận hiểu hành vi của ngăn giống như cửa sổ, hãy đặt tiêu đề mô tả cho các ngăn của ứng dụng. Các dịch vụ hỗ trợ tiếp cận sau đó có thể cung cấp thêm thông tin chi tiết cho người dùng khi giao diện hoặc nội dung của ngăn thay đổi.
ShareSheet( message = "Choose how to share this photo", modifier = Modifier .fillMaxWidth() .align(Alignment.TopCenter) .semantics { paneTitle = "New bottom sheet" } )
Để biết thêm thông tin về thuộc tính ngữ nghĩa paneTitle, hãy xem phần Thành phần giống cửa sổ.
Thành phần trang trí
Nếu một thành phần trong giao diện người dùng của bạn chỉ dành cho mục đích giãn cách hình ảnh hoặc giao diện thì hãy đặt các thuộc tính thích hợp trên thành phần đó để cho biết rằng các dịch vụ hỗ trợ tiếp cận có thể bỏ qua thành phần đó.
Đối với các thành phần kết hợp Image hoặc Icon, hãy đặt contentDescription = null. Đối với các thành phần khác chỉ mang tính trang trí và không cung cấp bối cảnh hoặc chức năng, bạn có thể sử dụng hideFromAccessibility. Thuộc tính ngữ nghĩa này yêu cầu các dịch vụ hỗ trợ tiếp cận bỏ qua mục này.
Nếu một composable tương tác chứa các phần tử con trang trí, không tương tác, hãy sử dụng clearAndSetSemantics để đảm bảo rằng các dịch vụ hỗ trợ tiếp cận không duyệt qua các phần tử đó. Xin lưu ý rằng clearAndSetSemantics sẽ xoá hoàn toàn ngữ nghĩa mặc định của một phần tử và các phần tử con của phần tử đó. Điều này cho phép bạn xác định một phần tử hỗ trợ tiếp cận mới, hợp nhất. Thông thường, bạn sử dụng phương pháp này cho các thành phần tuỳ chỉnh phức tạp.
Trong ví dụ sau, Icon và Text là các thành phần con trang trí
bên trong một nút bật/tắt tuỳ chỉnh. Để ngăn các dịch vụ hỗ trợ tiếp cận duyệt qua từng thành phần con này, bạn có thể xoá ngữ nghĩa của các thành phần đó bằng cách sử dụng clearAndSetSemantics trên thành phần mẹ Row. Điều này cho các dịch vụ hỗ trợ tiếp cận biết rằng cần xử lý toàn bộ Row dưới dạng một nút bật/tắt có thể duyệt qua:
// 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?") } }
Để biết thêm thông tin về cách xoá ngữ nghĩa, hãy xem phần Xoá và đặt ngữ nghĩa.
Thêm hành động hỗ trợ tiếp cận
Điều quan trọng là phải đảm bảo rằng người dùng dịch vụ hỗ trợ tiếp cận có cách để hoàn tất mọi luồng người dùng trong ứng dụng của bạn.
Nếu hoạt động tương tác của thành phần kết hợp tuỳ chỉnh thay đổi trạng thái của ứng dụng theo cách không rõ ràng, hãy cung cấp nhãn mô tả cho các thao tác nhấn tiêu chuẩn bằng cách sử dụng các tham số như onClickLabel hoặc onLongClickLabel trong Modifier.clickable hoặc Modifier.combinedClickable.
Đối với các hoạt động tương tác phức tạp không thể liên kết với các thao tác nhấn tiêu chuẩn, hãy sử dụng customActions.
Ví dụ: nếu ứng dụng của bạn cho phép người dùng kéo một mục đến vị trí khác hoặc vuốt vào một mục trong danh sách, thì bạn có thể cung cấp một cách khác để hoàn tất các luồng người dùng này bằng cách cho dịch vụ hỗ trợ tiếp cận thấy thao tác đó. Bằng cách này, người dùng TalkBack, Điều khiển bằng giọng nói hoặc Tiếp cận bằng công tắc có thể thực hiện các thao tác mà nếu không thì chỉ có thể thực hiện thông qua cử chỉ.
Trong Compose, bạn có thể xác định các thao tác hỗ trợ tiếp cận tuỳ chỉnh thông qua thuộc tính customActions trong đối tượng sửa đổi semantics, bằng cách sử dụng CustomAccessibilityAction.
Ví dụ: nếu ứng dụng của bạn cho phép người dùng vuốt vào một mục để đóng mục đó, thì bạn có thể hiển thị chức năng này thông qua một hành động hỗ trợ tiếp cận tuỳ chỉnh:
SwipeToDismissBox( modifier = Modifier.semantics { // Represents the swipe to dismiss for accessibility customActions = listOf( CustomAccessibilityAction( label = "Remove article from list", action = { removeArticle() true } ) ) }, state = rememberSwipeToDismissBoxState(), backgroundContent = {} ) { ArticleListItem() }
Sau khi triển khai thao tác hỗ trợ tiếp cận tuỳ chỉnh, người dùng có thể truy cập thao tác đó thông qua trình đơn thao tác.
Để biết thêm thông tin về thao tác tuỳ chỉnh, hãy xem bài viết Thao tác tuỳ chỉnh.
Hãy làm cho các hành động sẵn có dễ hiểu
Khi một phần tử trên giao diện người dùng hỗ trợ các thao tác như chạm và giữ, một dịch vụ hỗ trợ tiếp cận (chẳng hạn như TalkBack) sẽ thông báo đó là "Nhấn đúp và giữ để nhấn và giữ".
Thông báo chung này không cung cấp cho người dùng bất kỳ ngữ cảnh nào về thao tác chạm và giữ.
Để thông báo này hữu ích hơn, hãy chỉ định nội dung mô tả có ý nghĩa cho hành động.
Trong Compose, các đối tượng sửa đổi tương tác tiêu chuẩn như clickable và combinedClickable có các tham số tích hợp (cụ thể là onClickLabel và onLongClickLabel) mà bạn có thể dùng để cung cấp nội dung mô tả cho các hành động, như trong ví dụ sau:
var contextMenuPhotoId by rememberSaveable { mutableStateOf<Int?>(null) } val haptics = LocalHapticFeedback.current LazyVerticalGrid(columns = GridCells.Adaptive(minSize = 128.dp)) { items(photos, { it.id }) { photo -> ImageItem( photo, Modifier .combinedClickable( onClick = { activePhotoId = photo.id }, onLongClick = { haptics.performHapticFeedback(HapticFeedbackType.LongPress) contextMenuPhotoId = photo.id }, onLongClickLabel = stringResource(R.string.open_context_menu) ) ) } } if (contextMenuPhotoId != null) { PhotoActionsSheet( photo = photos.first { it.id == contextMenuPhotoId }, onDismissSheet = { contextMenuPhotoId = null } ) }
Khi đó, TalkBack sẽ thông báo: "Mở trình đơn theo bối cảnh", giúp người dùng nắm được mục đích của thao tác nhấn đúp.
Bạn cũng có thể chỉ định nhãn ngay trong đối tượng sửa đổi semantics.
Để biết thêm thông tin về cách phản hồi các thao tác nhấn và nhấp, hãy xem phần Nhấn và nhấn giữ và Thành phần tương tác.
Sử dụng bộ tính năng hỗ trợ tiếp cận tích hợp sẵn
Khi thiết kế giao diện người dùng của ứng dụng, hãy tận dụng bộ tính năng hỗ trợ tiếp cận tích hợp sẵn để tránh triển khai lại chức năng đã có. Theo mặc định, các API Material, Compose UI và Foundation triển khai và cung cấp nhiều phương pháp hỗ trợ tiếp cận.
Trong Jetpack Compose, hãy sử dụng các thành phần kết hợp tích hợp sẵn như Button, Switch và Checkbox để tạo giao diện người dùng dễ tiếp cận. Các thành phần này được đóng gói sẵn với các đối tượng sửa đổi semantics, chẳng hạn như role và stateDescription, mà bạn có thể sử dụng để giúp ứng dụng của mình dễ tiếp cận hơn.
Áp dụng ngữ nghĩa cho các thành phần tuỳ chỉnh
Khi tạo một thành phần tuỳ chỉnh, hãy lưu ý đến loại tính năng hỗ trợ tiếp cận mà thành phần này cần để thực hiện vai trò của mình. Thông thường, các API Compose chuẩn mà bạn đang sử dụng (chẳng hạn như clickable, toggleable hoặc selectable) là đủ vì chúng tự động điền sẵn cây ngữ nghĩa cho bạn.
Tuy nhiên, một số thành phần yêu cầu thông tin cụ thể hơn so với thông tin mà đối tượng sửa đổi tiêu chuẩn cung cấp. Trong những trường hợp này, hãy tìm các đối tượng sửa đổi chuyên biệt (như triStateToggleable) hoặc nếu không có, hãy cung cấp ngữ nghĩa một cách rõ ràng bằng cách sử dụng Modifier.semantics cấp thấp.
Ví dụ: hãy xem xét TriStateSwitch, một nút chuyển có ba trạng thái (Bật, Tắt và Không xác định).
Mặc dù đối tượng sửa đổi toggleable tiêu chuẩn giả định hai trạng thái, nhưng đối tượng sửa đổi triStateToggleable xử lý độ phức tạp của trạng thái thứ ba. Thành phần này tự động đặt khả năng hỗ trợ tiếp cận Role (Switch) và State. Bằng cách này, các dịch vụ hỗ trợ tiếp cận sẽ nhận được thông tin chính xác và bạn không cần phải xác định ngữ nghĩa theo cách thủ công.
Đoạn mã sau đây cho thấy một TriStateSwitch sử dụng phương pháp này:
@Composable fun TriStateSwitch( state: ToggleableState, onClick: () -> Unit, modifier: Modifier = Modifier ) { // A real implementation would include custom drawing for the switch. // This example uses a Box to demonstrate the semantics. Box( modifier = modifier .size(width = 64.dp, height = 40.dp) // triStateToggleable handles the semantics (Role and State) // automatically, so explicit Modifier.semantics is not needed here. .triStateToggleable( state = state, onClick = onClick, role = Role.Switch ) // Add visual feedback based on the state .background( when (state) { ToggleableState.On -> Color.Green ToggleableState.Off -> Color.Gray ToggleableState.Indeterminate -> Color.Yellow } ) ) } // Usage within another composable: var state by remember { mutableStateOf(ToggleableState.Off) } TriStateSwitch( state = state, onClick = { state = when (state) { ToggleableState.Off -> ToggleableState.Indeterminate ToggleableState.Indeterminate -> ToggleableState.On ToggleableState.On -> ToggleableState.Off } } )
Khi tạo một thành phần tuỳ chỉnh, hãy đảm bảo bạn cung cấp tất cả thuộc tính ngữ nghĩa có liên quan cho mục đích hỗ trợ tiếp cận. Ví dụ: nếu thành phần của bạn bắt chước một chế độ điều khiển tiêu chuẩn như nút chuyển hoặc nút, thì các thuộc tính này sẽ bao gồm vai trò của thành phần (chẳng hạn như Role.Switch hoặc Role.Button), stateDescription (chẳng hạn như "Bật", "Tắt", "Đã đánh dấu" hoặc "Chưa đánh dấu") và mọi nhãn hành động có liên quan. Để biết thêm thông tin, hãy xem bài viết Thành phần tuỳ chỉnh.
Dùng chú thích ngoài màu sắc
Để hỗ trợ người dùng mù màu, hãy sử dụng các tín hiệu khác ngoài màu sắc để phân biệt các thành phần giao diện người dùng trong màn hình của ứng dụng. Có thể kể đến những kỹ thuật như sử dụng nhiều hình dạng hoặc kích thước, cung cấp văn bản hoặc hình ảnh trực quan hoặc thêm phản hồi dựa trên âm thanh hoặc xúc giác (để đánh dấu sự khác biệt giữa các thành phần).
Hình 1 cho thấy hai phiên bản của một hoạt động. Một phiên bản chỉ sử dụng màu sắc để phân biệt hai thao tác có thể thực hiện trong một quy trình. Phiên bản còn lại áp dụng phương pháp tốt nhất là sử dụng màu và văn bản bên cạnh màu để làm nổi bật sự khác biệt giữa 2 lựa chọn:
Tăng khả năng hỗ trợ tiếp cận của nội dung đa phương tiện
Nếu bạn đang phát triển một ứng dụng chứa nội dung đa phương tiện, chẳng hạn như một đoạn video hoặc một bản ghi âm, hãy cố gắng hỗ trợ người dùng có nhu cầu hỗ trợ tiếp cận trong việc tìm hiểu nội dung này. Cụ thể, hãy thử làm như sau:
- Cung cấp các tuỳ chọn điều khiển cho phép người dùng tạm dừng hoặc dừng nội dung đa phương tiện, thay đổi âm lượng và bật/tắt phụ đề.
- Nếu video trình bày thông tin quan trọng để hoàn thành quy trình công việc, hãy cung cấp nội dung tương tự ở định dạng thay thế, chẳng hạn như bản chép lời.
Tài nguyên khác
Để biết thêm thông tin về cách giúp ứng dụng dễ tiếp cận hơn, hãy xem thêm các tài nguyên sau: