You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 05:01:11