喷气背包Compose:焦点样式

3
我正在制作自定义组件,我的自定义组件需要覆盖焦点状态。
在我的组件层次结构顶部,我使用以下代码跟踪焦点状态。
    val focused = remember { mutableStateOf(false) }
    val focusModifier = modifier.onFocusEvent {
        focused.value = it.hasFocus || it.isFocused
    }

    Component(modifier = focusModifier, focused = focused.value)

这个组件基本上就是这样的:
@Composable
fun Component(
    modifier: Modifier = Modifier,
    focused: Boolean = false
) {
   ...
   val colorStuff = if(focused) focusColors else otherColors
   var focusModifier = modifier
   if(focused) {
       focusModifier = modifier.border(BorderStroke(2.dp, Color.Red)).padding(16.dp)
   }
    NextComponent(
       focusModifier,
       colorStuff,
       etc
    )
}

如果我将代码中的colorStuff保留并且没有focusModifier代码,那么焦点状态正确完成,并且组件的颜色相应地改变。但是当我添加focusModifier代码并添加边框和填充时,焦点状态会触发,但接着立即消失。我认为这是因为修改器代码的添加改变了组件的构建顺序,使其放弃了焦点状态。但这也不完全合理。
实质上,我需要在焦点状态下为组件添加边框/阴影效果,因此这将是我需要多次执行的操作。目前我无法做到一次性完成。您有任何想法如何克服这个问题吗?

1
“焦点状态会被触发,但随即会立即丢失”是我在Compose中处理Focus Management时遇到的问题。任何一个失去焦点的组件都会向焦点管理器报告其失去的状态。当您开始聚焦一个组件并移动到下一个组件时,FocusManager将向您返回两个状态:一个失去焦点的组件和一个获得焦点的组件。我不知道您的用例是什么,但在我的情况下,我最终创建了一个具有唯一ID属性“isFocused:Boolean”的对象,以规避这些onFocusEvent{...}报告。 - z.g.y
你是否正在使用TextField? - Gabriele Mariotti
@GabrieleMariotti 这个特定的案例是一个按钮组件,但我要处理的许多组件都有类似的设计。 - Cayce K
2个回答

0

这不是对你问题的回答,但我想分享一下我的用例,也许能帮到你。请考虑以下代码:

data class PersonItem(
   val id : Int,
   val isActive: Boolean = false
)

列表项

@Composable
fun PersonInfoItem() {
Column {

    // First Name
    TextField(
        modifier = Modifier.onFocusEvent {
               // report to view model this personItem received focus events
        },
        value = "",
        onValueChange = {

        }
    )

    // Last Name
    TextField(
        modifier = Modifier.onFocusEvent {
              // report to view model this personItem received focus events
        },
        value = "",
        onValueChange = {

        }
    )
}

ViewModel

class ViewModel {

val currentFocusedItem : PersonItem? = null

fun onPersonItemFocused(personItem: PersonItem) {
    
    // this is where I validate that if the personItem argument
    // is not the same with the currentFocusedPerson, if they are not then the last one is not on focus anymore (set !isActive)
    // otherwise if they are the same, we are still on the same PersonItem
}

无论哪个TextField获得或失去焦点,只要我跟踪报告给ViewModelPersonItemId,我就保证如果是同一个PersonItem,它将在其Focus事件期间保留任何状态(例如,当任一TextField获得焦点时,该项将更改背景颜色,并在没有任何TextField聚焦时恢复其原始背景)。
另一件需要考虑的事情是,这些组件(即TextFields)在初始组合过程中会报告多个非活动焦点事件,这有点让人烦恼。

0
@Composable
fun FocusableButton(
    modifier: Modifier = Modifier,
    onClick: () -> Unit = {},
    label: String,
    enabled: Boolean = true,
    requestInitialFocus: Boolean = false
) {
    var backgroundColor by remember { mutableStateOf(FocusButtonUnselectedBackground) }
    var borderColor by remember { mutableStateOf(FocusButtonSelectedBorder) }
    fun applyColors(focusState: FocusState) {
        borderColor = if (focusState.isFocused) {
            FocusButtonSelectedBorder
        } else {
            FocusButtonUnselectedBorder
        }
        backgroundColor = if (focusState.isFocused) {
            FocusButtonSelectedBackground
        } else {
            FocusButtonUnselectedBackground
        }
    }

    val newModifier = if (requestInitialFocus) {
        val focusRequester = remember { FocusRequester() }
        LaunchedEffect(Unit) {
            focusRequester.requestFocus()
        }
        modifier
            .onFocusEvent(::applyColors)
            .focusRequester(focusRequester)
            .focusable()
    } else {
        modifier
            .onFocusEvent(::applyColors)
            .focusable()
    }

    OutlinedButton(
        onClick = { onClick() },
        modifier = newModifier,
        enabled = enabled,
        border = BorderStroke(1.dp, borderColor),
        colors = ButtonDefaults.buttonColors(backgroundColor = backgroundColor)
    ) {
        Text(text = label)
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接