【问题标题】:Why is my MutableState value reset inside LaunchedEffect为什么我的 MutableState 值在 LaunchedEffect 中重置
【发布时间】:2023-02-07 20:44:20
【问题描述】:

我正在尝试使用 kotlin jetpack compose 学习 android 编程,并且正在制作一个聊天应用程序。但是,我似乎无法在按下发送按钮后传递我的 TextField 值。

我正在使用我定义的自定义可点击功能:

@Composable
fun Modifier.timedClick(
    timeInMillis: Long = 2500,
    defaultPercentage: Float = 0f,
    content: String = "",
    onClick: (Float, String) -> Unit,
    interactionSource: MutableInteractionSource = remember{MutableInteractionSource()}
) = composed{
    var timeOfTouch = -1L
    Log.d("BUTTON", "#1 The content before LaunchedEffect is: $content")
    LaunchedEffect(interactionSource){
        Log.d("BUTTON", "#2 The content before LaunchedEffect is: $content")
        interactionSource.interactions.onEach {
            when(it){
                is PressInteraction.Press ->{
                    timeOfTouch = System.currentTimeMillis()
                    Log.d("BUTTON", "#3 The content after button pressed is: ${content}")
                    Log.d("BUTTON", "the button has been pressed")
                }
                is PressInteraction.Release -> {
                    val currentTime = System.currentTimeMillis()
                    val calculatedPercentage: Float = defaultPercentage+((currentTime-timeOfTouch).toFloat()/timeInMillis)*(1-defaultPercentage)
                    onClick(min(calculatedPercentage, 1f),
                        content
                    )
                    Log.d("BUTTON", "#4 The content after button is released is: ${content}")
                    Log.d("BUTTON", "the button has been released, the calculated percentage is ${calculatedPercentage * 100}% and the time is ${(currentTime-timeOfTouch).toFloat()/timeInMillis}ms")
                }
                is PressInteraction.Cancel -> {
                    timeOfTouch = -1L
                }
            }
        }
            .launchIn(this)
    }
    Modifier.clickable (
        interactionSource = interactionSource,
        indication = rememberRipple(),
        onClick = {}
    )

}

然后我使用图像作为按钮并像这样传递文本字段值

        var textFieldValue by remember { mutableStateOf("") }
        TextField(
            value = textFieldValue,
            onValueChange = { textFieldValue = it },
            modifier = Modifier.fillMaxWidth()
        )
        Box(modifier = Modifier
            .fillMaxWidth()
            .fillMaxHeight()){
            Image(
                modifier = Modifier
                    .clip(CircleShape)
                    .size(70.dp)
                    .align(Alignment.Center)
                    .timedClick(
                        content = textFieldValue,
                        onClick = onClick
                    ),
                contentDescription = null,
                painter = painterResource(id = R.drawable.poop3),
                contentScale = ContentScale.Crop
            )
        }

但是,每次发送时,LaunchedEffect 中的内容变量始终为空(即在 log.d #1 中,内容会定期正确更新,但 log.d #2 仅在运行开始时调用一次而 log.d #3 和 #4 将始终显示一个空字符串。我该如何解决这个问题?

【问题讨论】:

    标签: android kotlin android-jetpack-compose android-compose-textfield


    【解决方案1】:

    Launched 效果中的内容为空的原因是因为您正在使用状态为空时的值初始化 timedClick 函数。您可以通过将状态作为参数而不是值传递来解决此问题。

    为此,您必须在创建 textFieldValue 变量时将 by 替换为 =

    val textFieldValue = remember { mutableStateOf("") }
    TextField(
       value = textFieldValue.value,
       onValueChange = { textFieldValue.value = it },
       modifier = Modifier.fillMaxWidth()
     )
    

    timedClick 函数必须更改为以下内容:

    @Composable
    fun Modifier.timedClick(
        timeInMillis: Long = 2500,
        defaultPercentage: Float = 0f,
        content: MutableState<String>,
        onClick: (Float, String) -> Unit,
        interactionSource: MutableInteractionSource = remember{MutableInteractionSource()}
    ) = composed{
        var timeOfTouch = -1L
        Log.d("BUTTON", "#1 The content before LaunchedEffect is: ${content.value}")
        LaunchedEffect(interactionSource) {
            Log.d("BUTTON", "#2 The content before LaunchedEffect is: ${content.value}")
            interactionSource.interactions.onEach {
                when(it){
                    is PressInteraction.Press ->{
                        timeOfTouch = System.currentTimeMillis()
                        Log.d("BUTTON", "#3 The content after button pressed is: ${content.value}")
                        Log.d("BUTTON", "the button has been pressed")
                    }
                    is PressInteraction.Release -> {
                        val currentTime = System.currentTimeMillis()
                        val calculatedPercentage: Float = defaultPercentage+((currentTime-timeOfTouch).toFloat()/timeInMillis)*(1-defaultPercentage)
                        onClick(min(calculatedPercentage, 1f),
                            content.value
                        )
                        Log.d("BUTTON", "#4 The content after button is released is: ${content.value}")
                        Log.d("BUTTON", "the button has been released, the calculated percentage is ${calculatedPercentage * 100}% and the time is ${(currentTime-timeOfTouch).toFloat()/timeInMillis}ms")
                    }
                    is PressInteraction.Cancel -> {
                        timeOfTouch = -1L
                    }
                }
            }
                .launchIn(this)
        }
        Modifier.clickable (
            interactionSource = interactionSource,
            indication = rememberRipple(),
            onClick = {}
        )
    }
    

    编辑: 当您将状态作为参数传递时,它将传递对存储状态的内存位置的引用。当您在文本字段内键入时,此位置内的值将更新。这就是更新内容将在 timedClick 函数中可用的原因。

    【讨论】:

    • 谢谢!它现在可以工作了,但我仍然很困惑,在某些情况下我应该使用 remember 而不是 = remember 吗?
    • 没问题!如果您想将状态作为参数传递给其他可组合项,您应该使用 = remember。当您想要更改或使用另一个可组合项中的状态时,这可能很有用。当您只需要声明它的可组合项中的状态时,您可以使用 by remember。
    猜你喜欢
    • 2022-10-07
    • 2021-12-23
    • 2021-07-25
    • 1970-01-01
    • 2016-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多