为了帮助有无障碍需求的用户成功使用您的应用,请设计您的 来满足关键的无障碍要求
考虑最小触摸目标尺寸
屏幕上可供用户点击、触摸或可与用户互动的所有元素都应足够大,让用户能够进行可靠的互动。调整这些元素的尺寸时,请确保 将最小尺寸设置为 48dp,以正确遵循 Material Design 无障碍功能指南。
Material 组件,例如 Checkbox
、RadioButton
、Switch
Slider
和 Surface
- 在内部设置此最小尺寸,但仅限
该组件可以接收用户操作的时间。例如,当 Checkbox
的
其 onCheckedChange
参数设置为非 null 值,则该复选框会包含
内边距,宽度和高度至少为 48 dp。
@Composable private fun CheckableCheckbox() { Checkbox(checked = true, onCheckedChange = {}) }
当 onCheckedChange
参数设置为 null 时,内边距不会
因为无法直接与该组件交互。
@Composable private fun NonClickableCheckbox() { Checkbox(checked = true, onCheckedChange = null) }
在实现 Switch
、RadioButton
或
Checkbox
时,您通常会将可点击行为提升到父级容器,
将可组合项上的点击回调设置为 null
,并添加 toggleable
或
selectable
修饰符。
@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) } } }
当可点击可组合项的尺寸小于最小触摸目标尺寸时,Compose 仍会增加触摸目标尺寸。为此,它会将 可组合项边界之外的触摸目标尺寸。
以下示例包含一个非常小的可点击 Box
。触摸目标
区域会自动延伸到 Box
的边界之外,因此点按
Box
旁边的按钮仍会触发点击事件。
@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) ) } }
为防止不同可组合项的触摸区域之间可能发生的重叠,请始终
为可组合项使用足够大的最小尺寸。在本示例中,
也就是说,使用 sizeIn
修饰符设置内部框的最小尺寸:
@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) ) } }
添加点击标签
您可以使用点击标签为可组合项的点击行为添加语义。点击标签描述了当用户与 可组合项。无障碍服务使用点击标签来帮助描述应用, 有特定需求的用户
通过在 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 ) ) { // .. } }
或者,如果您无法访问可点击的修饰符,请将 semantics 修饰符中的点击标签:
@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) } ) { // .. } }
描述视觉元素
定义 Image
或 Icon
可组合项时,没有任何
使 Android 框架可以自动识别应用是什么
。您需要传递视觉元素的文字说明。
假设有一个屏幕,用户可以通过这个屏幕与朋友分享当前页面。此屏幕包含一个可点击的分享图标:
仅基于图标,Android 框架无法直观地描述该图标。 受影响的用户。Android 框架需要 图标。
contentDescription
参数用于描述视觉元素。使用本地化后的
字符串,因为它对用户可见。
@Composable private fun ShareButton(onClick: () -> Unit) { IconButton(onClick = onClick) { Icon( imageVector = Icons.Filled.Share, contentDescription = stringResource(R.string.label_share) ) } }
有些视觉元素纯粹只是装饰,您可能不想传达给
提供给用户将 contentDescription
参数设置为 null
时,
向 Android 框架指示此元素未与
操作或状态。
@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
由您决定。思考该元素是否传达了
用户执行任务所需的资源如果不是,最好将
输出内容。
合并元素
借助 TalkBack 和开关控制等无障碍服务,用户可以在屏幕上的各个元素之间移动焦点。请务必以正确的粒度聚焦元素。当界面中的每个低级可组合项 单独聚焦时,用户必须进行大量互动才能在屏幕上移动。 如果元素过于频繁地合并在一起,用户可能不知道 元素相辅相成
将 clickable
修饰符应用于可组合项时,Compose 会
自动合并可组合项包含的所有元素。这也适用于
ListItem
;列表项中的元素合并在一起,而无障碍功能
服务将其视为一个元素。
可能会有这样一组可组合项:它们组成了一个逻辑组,但该逻辑组不可点击,也不是列表项的组成部分。你仍需要使用无障碍功能 来将它们视为一个元素例如,假设有一个可组合项, 会显示用户的头像、姓名和一些额外信息:
您可以使用 mergeDescendants
让 Compose 合并这些元素
参数(位于 semantics
修饰符中)。这样,无障碍服务
仅选择合并的元素以及后代的所有语义属性
会合并在一起
@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") } } }
无障碍服务现在集中关注整个容器, 内容:
添加自定义操作
我们来看看以下列表项:
当您使用 TalkBack 等屏幕阅读器来听取 会先选中整个项,然后再选择书签图标
在长列表中,此操作可能需要反复执行多次。更好的方法是
定义可让用户为相应项添加书签的自定义操作。注意事项
您还必须明确删除
以确保无障碍服务不会被选中。本次
则是通过 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 { } ) } }
描述元素的状态
可组合项可以为符合以下条件的语义定义 stateDescription
:
Android 框架用于读出可组合项所处的状态。对于
例如,可切换的可组合项可以处于“已选中”状态,或“未选中”
状态。在某些情况下,您可能需要替换默认状态说明
Compose 使用的所有标签。为此,您可以明确指定
将可组合项定义为可切换的可组合项之前,先添加说明标签:
@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() } ) ) { /* ... */ } }
定义标题
应用有时会在可滚动容器的一个屏幕上显示大量内容。 例如,一个屏幕可以显示用户正在阅读的某篇文章的完整内容:
具有无障碍功能需求的用户难以浏览此类屏幕。援助 导航,指明哪些元素是标题。在前面的示例中,每个 子部分标题可定义为无障碍功能的标题。部分 借助 TalkBack 等无障碍服务,用户可以直接 。
在 Compose 中,您可以通过定义可组合项的
semantics
属性:
@Composable private fun Subsection(text: String) { Text( text = text, style = MaterialTheme.typography.headlineSmall, modifier = Modifier.semantics { heading() } ) }
处理自定义可组合项
每当您将应用中的某些 Material 组件替换为自定义组件 ,您必须牢记无障碍功能注意事项。
假设您要将 Material Checkbox
替换为自己的实现。
您可能会忘记添加 triStateToggleable
修饰符,
该组件的无障碍属性
一般来讲,您可以在 并模仿您可以发现的任何无障碍行为。 此外,大量使用 Foundation 修饰符,而不是界面级修饰符 修饰符,因为这些因素包括开箱即用的无障碍功能注意事项。
使用多个对象测试您的自定义组件实现 服务来验证其行为
其他资源
- 无障碍功能:基本概念和 所有 Android 应用开发通用的技术
- 构建无障碍应用:关键步骤 使您的应用使用起来更没有障碍
- 改进应用程序的原则 无障碍功能:确保实现无障碍功能的主要原则 提高应用的无障碍性
- 测试无障碍功能: Android 无障碍功能的测试原则和工具