My shopping list item Quantity TextField
allows a decimal point, and if the user types a decimal as the first character like ".42", it throws an NumberFormatException
when I cast the TextField
string to a Double in the code below. To prevent the exception, I simply check to see if the TextField
value is "."
. If so, I set the Quantity to an empty string and display a Toast
message telling the user to enter a valid quantity, but it is not setting the Quantity state to ""
, and pressing the decimal key at this point does not show the Toast
message. I have to press the backspace key and press the decimal key again to display the message. How can I fix this?
ShoppingListItem Model
@Parcelize
data class ShoppingListItem(
val id: Long = 0L,
val shoppingListId: Long = 0L,
...
val quantity: String = "",
): Parcelable
ViewModel
private val _shoppingListItemState = MutableLiveData(
savedStateHandle.get<ShoppingListItem>("shoppinglistitem")
?: ShoppingListItem()
)
override val shoppingListItemState: LiveData<ShoppingListItem>
get() = _shoppingListItemState
...
override fun setStateValue(stateToEdit: String, stateValue: Any?) {
when (stateToEdit) {
ITEM_QUANTITY_STR -> {
_shoppingListItemState.value =
_shoppingListItemState.value!!.copy(quantity = stateValue.toString())
}
}
}
Composable
CustomOutlinedTextField(
fieldModifier = Modifier
.width(150.dp)
.padding(end = 8.dp)
.onPreviewKeyEvent {
if (it.key == Key.Tab && it.nativeKeyEvent.action == ACTION_DOWN) {
focusManager.moveFocus(FocusDirection.Right)
true
} else {
false
}
},
label = ITEM_QUANTITY_STR,
inputVal = shoppingListItemState.value!!.quantity,
isSingleLine = true,
keyboardOptions = KeyboardOptions.Default.copy(
capitalization = KeyboardCapitalization.None,
autoCorrect = false,
keyboardType = KeyboardType.Decimal,
imeAction = ImeAction.Next
),
keyboardActions = KeyboardActions(
onNext = { focusManager.moveFocus(FocusDirection.Right) }
)
) { value ->
if(value == ".") {
Toast.makeText(context, "Please enter a valid quantity", Toast.LENGTH_LONG)
.show()
mainViewModel.setStateValue(
ITEM_QUANTITY_STR,
""
)
} else {
if (value.isEmpty()) {
mainViewModel.setStateValue(
ITEM_QUANTITY_STR,
""
)
} else {
val counter = value.count { it == decimalSeparator }
if (counter <= 1) {
if (value.toDouble() <= 999_999.999) {
mainViewModel.setStateValue(
ITEM_QUANTITY_STR,
value
)
}
}
}
}
}
CodePudding user response:
You can prevent the exception using in your validation something like:
value.toDoubleOrNull()
You can also use a regex to restrict the allowed character to a decimal number:
val pattern = remember { Regex("^\\d*\\.?\\d*\$") }
TextField(
value = text,
onValueChange = {
if (it.isEmpty() || it.matches(pattern)) {
text = it
validate(it.toDoubleOrNull())
}
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal)
)