Jetpack Compose中RequestFocus仅首次生效问题求助
Compose重复点击按钮无法触发TalkBack焦点重新定位的解决方案
问题场景
开发点击按钮刷新页面标题与内容的功能,根据无障碍要求,点击按钮后需将TalkBack焦点自动移至页面标题,让用户知晓新内容主题。使用FocusRequester实现时,首次点击按钮可正常切换焦点,但重复点击同一按钮(如刷新后再次点击)焦点无法切换,交替点击不同按钮则一切正常。
复现代码:
class MainActivity : ComponentActivity() { @OptIn(ExperimentalComposeUiApi::class) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { val (ref1, ref2) = remember { FocusRequester.createRefs() } Column( modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.SpaceBetween, ) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceAround ) { Focusable("Focusable #1", ref1) Focusable("Focusable #2", ref2) } Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceAround ) { Button( onClick = { ref1.requestFocus() }) { Text(text = "Focus on #1") } Button( onClick = { ref2.requestFocus() }) { Text(text = "Focus on #2") } } } } } } @Composable fun Focusable(text: String, focusRequester: FocusRequester) { Box( modifier = Modifier.height(100.dp), contentAlignment = Alignment.Center, ) { Text(text = text, modifier = Modifier .focusRequester(focusRequester) .focusable(true) .focusProperties { canFocus = true } .focusTarget()) } }
问题原因
当目标元素已经处于焦点状态时,调用FocusRequester.requestFocus()不会触发TalkBack的焦点重新播报——系统判定焦点未发生变化,因此不会执行焦点移动的无障碍逻辑。但实际场景中内容已更新,需要强制让TalkBack重新关注该元素并播报新内容。
解决方案
方案1:先清除焦点再请求焦点
通过LocalFocusManager先清除当前焦点,再请求目标元素的焦点,强制触发焦点变化逻辑,让TalkBack重新播报:
修改按钮的onClick逻辑:
Button( onClick = { val focusManager = LocalFocusManager.current // 清除当前焦点 focusManager.clearFocus() // 重新请求目标焦点 ref1.requestFocus() }) { Text(text = "Focus on #1") }
方案2:处理焦点已存在的情况,强制播报内容
如果不想清除全局焦点,可以判断焦点请求是否成功,若失败(说明目标已处于焦点状态),则直接触发TalkBack播报元素内容:
Button( onClick = { val context = LocalContext.current val success = ref1.requestFocus() if (!success) { // 目标已在焦点状态,强制播报内容 context.announceForAccessibility("Focusable #1") // 可选:触发焦点事件,确保TalkBack重新识别 val view = LocalView.current val semanticsOwner = view.findSemanticsOwner() semanticsOwner?.findFocusedSemanticNode()?.let { node -> node.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) } } }) { Text(text = "Focus on #1") }
方案3:结合内容更新触发焦点请求(适配实际业务场景)
在实际刷新内容的场景中,可将焦点请求与内容状态绑定,通过LaunchedEffect在内容更新时自动处理焦点:
@Composable fun Title(text: String, focusRequester: FocusRequester) { val focusManager = LocalFocusManager.current // 当标题文本变化时,重新请求焦点 LaunchedEffect(text) { focusManager.clearFocus() focusRequester.requestFocus() } Text( text = text, modifier = Modifier .focusRequester(focusRequester) .focusable(true) .focusTarget() ) }
这样每次内容刷新(标题文本变化)时,都会自动触发焦点重新定位,无需手动在按钮点击中处理。
内容的提问来源于stack exchange,提问作者metz82
相关产品推荐
相关产品推荐

