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

Jetpack Compose自定义焦点指示在交互后消失的问题求解

解决Jetpack Compose自定义焦点指示丢失问题

问题根源

  1. 焦点状态跟踪逻辑不可靠:自定义MyHighlightIndicationNode用本地变量isFocused和手动计数focusCount跟踪焦点,无法应对Compose重组或toggleable/clickable这类复杂交互流,容易出现状态丢失。
  2. 系统默认交互清除焦点:点击可交互组件时,系统会自动清除键盘焦点,导致焦点边框消失。
  3. 重组时状态未持久化:本地变量isFocused不属于Compose快照状态,组件重组后会被重置为初始值。

解决方案

1. 重构自定义焦点指示节点,使用官方状态API跟踪焦点

替换手动计数逻辑,改用官方提供的collectIsFocusedAsState(),该API基于Compose快照状态,能在重组时稳定保持焦点状态:

private class MyHighlightIndicationNode(
    private val interactionSource: InteractionSource,
    private val themedIndicationColor: Color
) : Modifier.Node(), DrawModifierNode {
    // 用快照状态保存焦点状态,重组时不会丢失
    private val isFocused by interactionSource.collectIsFocusedAsState()

    override fun ContentDrawScope.draw() {
        drawContent()
        if (isFocused) {
            drawRoundRect(
                color = themedIndicationColor,
                size = size,
                cornerRadius = CornerRadius(12.dp.toPx(), 12.dp.toPx()),
                style = Stroke(width = 2.dp.toPx()),
                alpha = 1.0f
            )
        }
    }
}

class MyHighlightIndication(private val themedIndicationColor: Color) : IndicationNodeFactory {
    override fun create(interactionSource: InteractionSource): DelegatableNode {
        return MyHighlightIndicationNode(interactionSource, themedIndicationColor)
    }

    override fun hashCode(): Int = themedIndicationColor.hashCode()

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false
        other as MyHighlightIndication
        return themedIndicationColor == other.themedIndicationColor
    }
}

2. 配置可交互组件,操作后强制保持焦点

使用FocusRequester管理焦点,在toggleable/clickable操作完成后手动请求焦点,覆盖系统默认的清除焦点行为:

@Composable
private fun GoodExample7(
    snackbarLauncher: SnackbarLauncher?
) {
    val cardClickMessage = stringResource(id = R.string.custom_focus_indicators_example_7_message)
    val interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
    var isCheck by remember { mutableStateOf(false) }
    // 创建焦点请求器,用于手动控制焦点
    val focusRequester = remember { FocusRequester() }

    OutlinedCard(
        onClick = {
            snackbarLauncher?.show(cardClickMessage)
            // 点击后重新请求焦点,避免焦点丢失
            focusRequester.requestFocus()
        },
        modifier = Modifier
            .padding(top = 8.dp)
            .focusRequester(focusRequester)
            .focusable(true) // 确保组件具备获取焦点的能力
            .indication(
                interactionSource = interactionSource,
                indication = MyHighlightIndication(
                    themedIndicationColor = MaterialTheme.colorScheme.primary
                )
            )
            .toggleable(
                value = isCheck,
                onValueChange = {
                    isCheck = it
                    // 切换状态后重新请求焦点
                    focusRequester.requestFocus()
                },
                role = Role.Checkbox,
                interactionSource = interactionSource,
                indication = ripple()
            ),
        interactionSource = interactionSource
    ) {
        Row (
            horizontalArrangement = Arrangement.Center,
            modifier = Modifier.padding(horizontal = 12.dp),
            verticalAlignment = Alignment.CenterVertically
        ) {
            BodyText(
                textId = R.string.custom_focus_indicators_example_7_card_description,
                modifier = Modifier
            )
            Checkbox(checked = isCheck, onCheckedChange = null)
        }
    }
}

3. 额外优化点

  • 确保所有关联的可交互组件共享同一个interactionSource,保持交互状态的一致性。
  • 全局界面更新(如进度条刷新)时,由于焦点状态基于快照状态管理,会自动同步,不会出现丢失情况。

关键说明

  • collectIsFocusedAsState()是官方提供的可靠API,无需手动处理FocusInteraction流,能精准跟踪组件焦点状态。
  • FocusRequester强制在交互操作后重新获取焦点,解决了系统默认的"点击清除焦点"问题。
  • 使用Compose快照状态存储焦点状态,彻底避免重组导致的状态丢失。

内容的提问来源于stack exchange,提问作者Amit Kundu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 13:04:56