最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

android jetpack compose - Custom Modifier.indication loose it's drawing if there is "toggleableclickableselecta

programmeradmin0浏览0评论

So I'm trying to improve the keyboard focus indication for Jetpack Compose components.

I have created a custom class to draw border:

private class MyHighlightIndicationNode(private val interactionSource: InteractionSource) :
    Modifier.Node(), DrawModifierNode {
    private var isFocused = false

    override fun onAttach() {
        coroutineScope.launch {
            var focusCount = 0
            interactionSource.interactions.collect { interaction ->
                when (interaction) {
                    is FocusInteraction.Focus -> focusCount++
                    is FocusInteraction.Unfocus -> focusCount--
                }
                val focused = focusCount > 0
                if (isFocused != focused) {
                    isFocused = focused
                    invalidateDraw()
                }
            }
        }
    }

    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
            )
        }
    }
}

object MyHighlightIndication : IndicationNodeFactory {
    override fun create(interactionSource: InteractionSource): DelegatableNode {
        return MyHighlightIndicationNode(interactionSource)
    }

    override fun hashCode(): Int = -1

    override fun equals(other: Any?) = other === this
}

It's from this link

Now, if a Modifier has a toggleable/clickable/selectable interaction, I saw that the border gets removed after the action.

Here is client code:

@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) }

    OutlinedCard(
        onClick = {
            snackbarLauncher?.show(cardClickMessage)
        },
        modifier = Modifier
            .padding(top = 8.dp)
            .indication(
                interactionSource = interactionSource,
                indication = VisibleFocusIndication(
                    themedIndicationColor = MaterialTheme.colorScheme.primary
                )
            ).toggleable(
                value = isCheck,
                onValueChange = { isCheck = it },
                role = Role.Checkbox,
                interactionSource = interactionSource,
                indication = ripple()
            )
        ,
        // Key technique: Provide a common interactionSource to both clickable Card and indication.
        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)
        }
    }
}

Before Tap:

After Click enter:

Expected: After the click event, it should keep showing the Border indication. An additional issue is if there is some other update, say, a progress bar update, in those cases, it also loses the focus from the element.

I've used some boilerplate code. Any idea how this can be solved?

So I'm trying to improve the keyboard focus indication for Jetpack Compose components.

I have created a custom class to draw border:

private class MyHighlightIndicationNode(private val interactionSource: InteractionSource) :
    Modifier.Node(), DrawModifierNode {
    private var isFocused = false

    override fun onAttach() {
        coroutineScope.launch {
            var focusCount = 0
            interactionSource.interactions.collect { interaction ->
                when (interaction) {
                    is FocusInteraction.Focus -> focusCount++
                    is FocusInteraction.Unfocus -> focusCount--
                }
                val focused = focusCount > 0
                if (isFocused != focused) {
                    isFocused = focused
                    invalidateDraw()
                }
            }
        }
    }

    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
            )
        }
    }
}

object MyHighlightIndication : IndicationNodeFactory {
    override fun create(interactionSource: InteractionSource): DelegatableNode {
        return MyHighlightIndicationNode(interactionSource)
    }

    override fun hashCode(): Int = -1

    override fun equals(other: Any?) = other === this
}

It's from this link

Now, if a Modifier has a toggleable/clickable/selectable interaction, I saw that the border gets removed after the action.

Here is client code:

@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) }

    OutlinedCard(
        onClick = {
            snackbarLauncher?.show(cardClickMessage)
        },
        modifier = Modifier
            .padding(top = 8.dp)
            .indication(
                interactionSource = interactionSource,
                indication = VisibleFocusIndication(
                    themedIndicationColor = MaterialTheme.colorScheme.primary
                )
            ).toggleable(
                value = isCheck,
                onValueChange = { isCheck = it },
                role = Role.Checkbox,
                interactionSource = interactionSource,
                indication = ripple()
            )
        ,
        // Key technique: Provide a common interactionSource to both clickable Card and indication.
        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)
        }
    }
}

Before Tap:

After Click enter:

Expected: After the click event, it should keep showing the Border indication. An additional issue is if there is some other update, say, a progress bar update, in those cases, it also loses the focus from the element.

I've used some boilerplate code. Any idea how this can be solved?

Share Improve this question edited Feb 6 at 12:12 Amit Kundu asked Feb 6 at 11:56 Amit KunduAmit Kundu 1196 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

I have found the issue. The issue was I was creating an instance of the Focusable class. Without remember scope. So the class was rerendering resulting is loosing focus.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论