androidkotlinandroid-jetpack-composeandroid-compose-textfield

Jetpack compose thousands separator visual transformation that also works with a decimal


How to implement a thousands separator visual transformation which also works with decimals too. I have found an implementation of thousand separator visual transformation for Int numbers but the problem is when I want to use it for decimal numbers which I have to control the count of decimal separator not to exceed more than 1 time.

Implementation link


Solution

  • You can use:

    Something like:

    val pattern = remember { Regex("^\\d*\\.?\\d*\$") }
    
    TextField(
        value = text,
        onValueChange = {
            if (it.isEmpty() || it.matches(pattern)) {
                text = it
            }
        },
        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal),
        visualTransformation = ThousandSeparatorTransformation()
    )
    
    class ThousandSeparatorTransformation : VisualTransformation {
        override fun filter(text: AnnotatedString): TransformedText {
    
            val symbols = DecimalFormat().decimalFormatSymbols
            val decimalSeparator = symbols.decimalSeparator
    
            var outputText = ""
            var integerPart = 0L
            var decimalPart = ""
    
            if (text.text.isNotEmpty()) {
                val number = text.text.toDouble()
                integerPart = number.toLong()
                outputText += NumberFormat.getIntegerInstance().format(integerPart)
                if (text.text.contains(decimalSeparator)) {
                    decimalPart = text.text.substring(text.text.indexOf(decimalSeparator))
                    if (decimalPart.isNotEmpty()) {
                        outputText += decimalPart
                    }
                }
            }
    
            val numberOffsetTranslator = object : OffsetMapping {
                override fun originalToTransformed(offset: Int): Int {
                    return outputText.length
                }
    
                override fun transformedToOriginal(offset: Int): Int {
                    return text.length
                }
            }
    
            return TransformedText(
                text = AnnotatedString(outputText),
                offsetMapping = numberOffsetTranslator
            )
        }
    }
    

    With this OffsetMapping the cursor remains stationary at the end of the value. Otherwise you have to calculate the thousandsSeparatorCount and fix the offset according to it.

    enter image description here