Search code examples
androidkotlindecimalformatandroid-textwatcheraddtextchangedlistener

Format a number string into 2 decimal double independent of the number string length


In Android application developed using Kotlin, there have an EditText which accepts number only which is considered as USD. The input needs to be formatted into 2 decimals so that the input needs to be formatted as below

  • 7 -> 0.07
  • 73 -> 0.73
  • 736 -> 7.36

Tried using input filter. Input filter is also used to limit max value and single decimal input entry.

editTextField.filters =
            arrayOf(DecimalInputFilter())

class DecimalDigitsInputFilter() : InputFilter {
    override fun filter(
    source: CharSequence?,
    start: Int,
    end: Int,
    dest: Spanned?,
    dstart: Int,
    dend: Int
    ): CharSequence? {}

}

Couldn't manage to get the number formatted. Able to restrict inputs based on the rule.

editTextField.addTextChangedListener(object : TextWatcher{
   override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
        print("beforeTextChanged")
  }

  override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
       print("onTextChanged")
       val inputFormatter = DecimalFormat("0.00")
       inputFormatter.isDecimalSeparatorAlwaysShown = true
       inputFormatter.minimumFractionDigits = 2
       editTextField.setText((s.toString()).format(inputFormatter))
  }

  override fun afterTextChanged(s: Editable?) {
       print("afterTextChanged")
  }
    
  })

This also fails.


Solution

  • I think the main problem is that you are setting a text to the EditText inside of a TextWatcher which leads to looping recursion and then stack-overflow. You should change the text wrapped in removing and adding again the TextWatcher. Here is a simple solution:

    editTextField.addTextChangedListener(object : TextWatcher {
        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
            print("beforeTextChanged")
        }
    
        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
            print("onTextChanged")
    
            val newValue = s.toString()
                .takeIf { it.isNotBlank() }
                ?.replace(".", "")
                ?.toDouble() ?: 0.0
    
            editTextField.let {
                it.removeTextChangedListener(this)
                it.setText(String.format("%.2f", newValue / 100))
                it.setSelection(it.text?.length ?: 0)
                it.addTextChangedListener(this)
            }
        }
    
        override fun afterTextChanged(s: Editable?) {
            print("afterTextChanged")
        }
    })