如何使屏幕阅读器可以访问 ClickableText
How to make ClickableText accessible to screen readers
此代码在 Jetpack Compose Composable 中创建一个 ClickableText 元素:
ClickableText(
text = forgotPasswordAnnotatedString,
onClick = {
context.startActivity(intent)
},
modifier = Modifier
.padding(top = mediumPadding)
)
这里定义了带注释的字符串,使文字看起来像link:
val forgotPasswordAnnotatedString = buildAnnotatedString {
append(stringResource(R.string.forgot_password))
addStyle(
style = SpanStyle(
textDecoration = TextDecoration.Underline,
color = Color.White,
fontSize = 16.sp,
fontWeight = FontWeight.Medium
),
start = 0,
end = 21,
)
}
当我在 Android 中使用 TalkBalk 屏幕 reader 遇到此文本时,屏幕 reader 没有明确表示这是可点击的文本,点击后会执行某些操作. reader 只是阅读文本。
有没有办法让屏幕清楚 reader 此文本是交互式的?否则我应该只使用一个按钮并将其设置为看起来像 link?
将 .semantics.contentDescription 添加到修改器会更改屏幕上读取的内容 reader。我不得不使用 contentDescription 来明确表示这是 link 来重置您的密码。
屏幕 reader 仍然无法识别可点击的元素,但希望描述有助于向用户传达该元素是交互式的。
ClickableText(
text = forgotPasswordAnnotatedString,
onClick = {
context.startActivity(intent)
},
modifier = Modifier
.padding(top = mediumPadding)
// new code here:
.semantics {
contentDescription = "Forgot your password? link"
}
)
看起来你的意图是让整个文本都可以点击?您最好的选择可能是 TextButton,如建议的那样
加布里埃尔·马里奥蒂。
但是,如果您不仅希望 link 的一部分可点击,或有多个可点击部分,我能找到的最好方法是在文本。这意味着我可以将可点击区域的触摸目标控制在至少 48.dp
并且可以使用 semantics{}
修饰符来控制屏幕 reader 解释它的方式。
欢迎任何建议。
// remember variables to hold the start and end position of the clickable text
val startX = remember { mutableStateOf(0f) }
val endX = remember { mutableStateOf(0f) }
// convert to Dp and work out width of button
val buttonPaddingX = with(LocalDensity.current) { startX.value.toDp() }
val buttonWidth = with(LocalDensity.current) { (endX.value - startX.value).toDp() }
Text(
text = forgotPasswordAnnotatedString,
onTextLayout = {
startX.value = it.getBoundingBox(0).left // where 0 is the start index of the range you want to be clickable
endX.value = it.getBoundingBox(21 - 1).right // where 21 is the end index of the range you want to be clickable
}
)
请注意,buttonPaddingX 是相对于文本位置而不是 Window,因此您可能必须将两者包围在 Box{} 中或使用 ConstraintLayout。
然后绘制隐形框
Box(modifier = Modifier
.sizeIn(minWidth = 48.dp, minHeight = 48.dp) // minimum touch target size
.padding(start = buttonPaddingX)
.width(buttonWidth)
// .background(Color.Magenta.copy(alpha = 0.5f)) // uncomment this to debug where the box is drawn
.clickable(onClick = { context.startActivity(intent) })
.semantics {
// tell TalkBack whatever you need to here
role = Role.Button
contentDescription = "Insert button description here"
}
)
在我的代码中,我使用 pushStringAnnotation(TAG, annotation)
而不是直接引用字符串索引。这样我就可以用 annotatedString.getStringAnnotations(TAG,0,annotatedString.length).first()
获取可点击区域的开始和结束索引。如果文本中有多个 link 则很有用。
令人失望的是,ClickableText 从一开始就没有考虑到可访问性,希望我们能够在未来的更新中再次使用它。
此代码在 Jetpack Compose Composable 中创建一个 ClickableText 元素:
ClickableText(
text = forgotPasswordAnnotatedString,
onClick = {
context.startActivity(intent)
},
modifier = Modifier
.padding(top = mediumPadding)
)
这里定义了带注释的字符串,使文字看起来像link:
val forgotPasswordAnnotatedString = buildAnnotatedString {
append(stringResource(R.string.forgot_password))
addStyle(
style = SpanStyle(
textDecoration = TextDecoration.Underline,
color = Color.White,
fontSize = 16.sp,
fontWeight = FontWeight.Medium
),
start = 0,
end = 21,
)
}
当我在 Android 中使用 TalkBalk 屏幕 reader 遇到此文本时,屏幕 reader 没有明确表示这是可点击的文本,点击后会执行某些操作. reader 只是阅读文本。
有没有办法让屏幕清楚 reader 此文本是交互式的?否则我应该只使用一个按钮并将其设置为看起来像 link?
将 .semantics.contentDescription 添加到修改器会更改屏幕上读取的内容 reader。我不得不使用 contentDescription 来明确表示这是 link 来重置您的密码。
屏幕 reader 仍然无法识别可点击的元素,但希望描述有助于向用户传达该元素是交互式的。
ClickableText(
text = forgotPasswordAnnotatedString,
onClick = {
context.startActivity(intent)
},
modifier = Modifier
.padding(top = mediumPadding)
// new code here:
.semantics {
contentDescription = "Forgot your password? link"
}
)
看起来你的意图是让整个文本都可以点击?您最好的选择可能是 TextButton,如建议的那样 加布里埃尔·马里奥蒂。
但是,如果您不仅希望 link 的一部分可点击,或有多个可点击部分,我能找到的最好方法是在文本。这意味着我可以将可点击区域的触摸目标控制在至少 48.dp
并且可以使用 semantics{}
修饰符来控制屏幕 reader 解释它的方式。
欢迎任何建议。
// remember variables to hold the start and end position of the clickable text
val startX = remember { mutableStateOf(0f) }
val endX = remember { mutableStateOf(0f) }
// convert to Dp and work out width of button
val buttonPaddingX = with(LocalDensity.current) { startX.value.toDp() }
val buttonWidth = with(LocalDensity.current) { (endX.value - startX.value).toDp() }
Text(
text = forgotPasswordAnnotatedString,
onTextLayout = {
startX.value = it.getBoundingBox(0).left // where 0 is the start index of the range you want to be clickable
endX.value = it.getBoundingBox(21 - 1).right // where 21 is the end index of the range you want to be clickable
}
)
请注意,buttonPaddingX 是相对于文本位置而不是 Window,因此您可能必须将两者包围在 Box{} 中或使用 ConstraintLayout。
然后绘制隐形框
Box(modifier = Modifier
.sizeIn(minWidth = 48.dp, minHeight = 48.dp) // minimum touch target size
.padding(start = buttonPaddingX)
.width(buttonWidth)
// .background(Color.Magenta.copy(alpha = 0.5f)) // uncomment this to debug where the box is drawn
.clickable(onClick = { context.startActivity(intent) })
.semantics {
// tell TalkBack whatever you need to here
role = Role.Button
contentDescription = "Insert button description here"
}
)
在我的代码中,我使用 pushStringAnnotation(TAG, annotation)
而不是直接引用字符串索引。这样我就可以用 annotatedString.getStringAnnotations(TAG,0,annotatedString.length).first()
获取可点击区域的开始和结束索引。如果文本中有多个 link 则很有用。
令人失望的是,ClickableText 从一开始就没有考虑到可访问性,希望我们能够在未来的更新中再次使用它。