【问题标题】:Compose TextField shows wierd behavior撰写 TextField 显示奇怪的行为
【发布时间】:2021-12-15 12:12:21
【问题描述】:

我正在尝试实现一个 TextField,它输入一个数量并在键入后立即对其进行格式化,并将其限制为 100,000。

@Composable
fun MainScreen(
    viewModel: MyViewModel
) {
    val uiState by viewModel.uiState.collectAsState()
    Column {
        AmountSection(
            uiState.amount,
            viewModel::updateAmount
        )
        Text(text = viewModel.logs)
    }
}

@Composable
fun AmountSection(
    amount: TextFieldValue,
    updateAmount: (TextFieldValue) -> Unit
) {
    BasicTextField(
        value = amount,
        onValueChange = updateAmount,
        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number
    )
)

MyViewModel:

class MyViewModel: ViewModel() {

    private val _uiState = MutableStateFlow(MyUiState())
    val uiState: StateFlow<MyUiState> = _uiState

    var logs by mutableStateOf("")
    var text = ""

    fun updateAmount(amount: TextFieldValue) {
        val formattedAmount: String = amount.text.getFormattedAmount()
        text += "input = ${amount.text}\n"
        text += "output = $formattedAmount \n"
        logs = text
        _uiState.update {
            it.copy(amount = TextFieldValue(formattedAmount, TextRange(formattedAmount.length))
        }
    }    
}

data class MyUiState(val amount: TextFieldValue = TextFieldValue())

(logs 和 text 仅用于记录目的。发现很难共享 logcat 输出,因此以这种方式呈现)

结果:

  • 当我按 6 时,输入是预期的“12,3456”(忽略货币)
  • 我的getFormattedAmount() 函数将最后六个删除为 (123456 > 100000)。它输出“12,345”,这也是正确的。屏幕上显示的是“12,345”。
  • 但是当我按 7 时,我得到输入“12,34567”。那个6是哪里来的??它不在uiState.amount 中。

(请忽略最后一个输出行。getFormattedAmount 仅在数量超过限制时删除最后一个字符,并且它给出了错误的输出,因为它没有预料到那个输入)

我觉得我在这里犯了一些非常愚蠢的错误,如果有人能帮助我找出答案,我将非常感激。

【问题讨论】:

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


【解决方案1】:

根据编辑进行编辑:- 从问题的外观来看,您希望在这里实现什么并不是很清楚,但这是我的推论 - 您只需要一个允许输入数字的 TextField,但只能输入最大值(VALUE,而不是字符)。当用户按下数字导致值超过您的最大值时,您当然希望不输入该数字,但您希望在这种情况下完全不反映任何变化,即字段值应保持不变。

根据上面的推论,下面是一个例子:-

首先,你的 uiState 变量。为了清楚起见,我保持简单。

class VM: ViewModel(){
 var fieldValue by mutableStateOf("")
 
 fun onFieldUpdate(newValue){
   if(newValue.toDouble() > 999999999999999)
   return
   else
   fieldValue = newValue
 }
}

@Composable
fun CrazyField(fieldValue: String, onFieldUpdate: (String) -> Unit){
 TextField(value = fieldValue, onValueChange = onFieldUpdate)
}

如果没有实际运行,请勿进一步评论。

原答案:- 使用双精度解析器。

var text by remember { mutableStateOf("") }
TextField(
    value = text,
    onValueChange = {
        if (it.toDouble() <= 100000)
            text = it //Based on your use-case - it won't cut off text or limit the amount of characters
       else text = it.subString(0,7) //In-case of pasting or fast typing
    },
    keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
)

【讨论】:

  • 如果我得到了这种疯狂行为的解释,我将能够实现我所需要的(希望如此)。否则,如果我能获得该字段,我会将其复制粘贴到我的代码中并继续前进。 (希望以后不会遇到这样的问题?)
  • 好吧,我认为本机问题是因为您使用的自定义数据对象。试着用我已经说过的变量替换它。我遇到了自定义类型的问题,这些自定义类型可能被 Compose 视为可变类型,因此无法积极观察。当您使用预定义的不可变类型(如 String 和 Int)时,可以很容易地观察到它们,并且可以确保在正确的时间戳上发生正确的重组。
【解决方案2】:

在 Sean 的 Compose slack 频道上找到 this 评论。

作为一个模型,你应该假设键盘可以任意和大 编辑每个onValueChange。例如,如果用户使用 自动更正,用表情符号替换单词或其他智能编辑功能。到 正确处理这个问题,编写任何转换逻辑,假设 传递给onValueChange 的当前文本与上一个或下一个无关 将传递给 onValueChange 的值。

所以这是 TextField 和 IME 关系的一些问题。
我重写了我的getFormattedAmount 函数来格式化给定的字符串,而不做任何假设(之前假设金额被格式化到最后第二个字符)。现在一切似乎都已解决。

【讨论】:

  • 如果有人感兴趣,我可以分享我的新功能。现在不包括在内,因为这不是这个问题的主要焦点。
  • 要么它已经在上面的 cmets 中提到过,要么因为它看起来太明显而没有提到。你似乎真的很困惑你在这里想要什么。您要将此标记为答案吗?这并不能真正解释它仍然值得怀疑的怪异行为。我的建议是让问题保持开放。
  • 我正准备将其标记为已接受。但如果你这么说,让我们保持开放。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-07-12
  • 2016-02-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-10
相关资源
最近更新 更多