0

I need to create a custom numeric keyboard in Android Studio using Kotlin. I successfully implemented the view with keyboard buttons (from 0 to 9 and one delete button) and TextInputEditText that shows the result. I implemented also the buttons in my fragment:

result = binding.editTextInsert
        // Data input buttons
        val button0: AppCompatButton = binding.button0
        ...
        val buttonDelete: ImageButton = binding.buttonDelete

        val listener = View.OnClickListener { v ->
            val b = v as AppCompatButton
            result.append(b.text)
        }

        button0.setOnClickListener(listener)
        ...

But I want to auto format the TextInputEditText in a decimal format 0.00:

if a user press 1 it becomes 0.01 instead of 1

if he entered 100, then the value in the TextInputEditText must be formatted as 1.00.

I assume this can be done by using TextWatcher and loop but I don't know how to achieve this.

EDIT Correct answer based from here

                binding.editTextInsert.addTextChangedListener(object: TextWatcher {
            override fun afterTextChanged(s: Editable?) {
                var current: String = ""
                if (s.toString() != current) {
                    result.removeTextChangedListener(this)
                    val cleanString: String = s!!.replace("""[$,.]""".toRegex(), "")
                    val parsed: BigDecimal =
                        BigDecimal(cleanString).setScale(2, BigDecimal.ROUND_FLOOR)
                            .divide(BigDecimal(100), BigDecimal.ROUND_FLOOR)
                    val formatted: String = NumberFormat.getCurrencyInstance().format(parsed)
                    current = parsed.toString()
                    result.setText(parsed.toString())
                    result.setSelection(parsed.toString().length)
                    result.addTextChangedListener(this)
                }
            }
            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { }
            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { }
        })
Ozzini
  • 145
  • 1
  • 7
  • 1
    if you want android answers, tag your question with android :) android studio is just the IDE used to develop android apps, more people will see it if you use the correct tags – a_local_nobody Jul 12 '21 at 12:45

1 Answers1

1

Try the following

YOUR_EDIT_TEXT.addTextChangedListener {
        if (it != null && it.isNotEmpty()) {
            val digitsInEditable = Regex("[^\\d]").replace(it.toString(), "").toDoubleOrNull()
            if (digitsInEditable != null) {
                val textBuilder = StringBuilder("$")
                textBuilder.append(String.format("%.2f", digitsInEditable / 100))
                val newText = textBuilder.toString()
                if (newText != it.toString()) {
                    it.replace(0, it.length, newText)
                }
            } else {
                it.replace(0, it.length, "")
            }
        }
    }

It would be better if you add a hint to 0.00 instead adding a default text to 0.00.

Rahul Rawat
  • 360
  • 2
  • 9
  • Thank you @Rahul Rawat but there's an error: Type mismatch: inferred type is () -> Unit but TextWatcher! was expected. Looks like TextWatcher is necessary – Ozzini Jul 12 '21 at 10:59
  • Yeah it's just a helpful extension function inside androidX but yeah go ahead and use the same code inside afterTextChanged. And accept this if it works. – Rahul Rawat Jul 12 '21 at 11:10
  • When I put this code into TextWatcher inside afterTextChanged I get unresolved reference "it". When I replace every "it" with binding.id of my edit text I get a bunch of errors which I don't understand: "Unresolved reference. None of the following candidates is applicable because of receiver type mismatch: public inline fun Array.isNotEmpty(): Boolean defined in kotlin.collections, public inline fun BooleanArray.isNotEmpty(): Boolean defined in kotlin.collections (...)" etc – Ozzini Jul 12 '21 at 11:23
  • I said add it in afterTextChanged not onTextChanged and change the only parameter that method requires name to it or vice versa – Rahul Rawat Jul 12 '21 at 11:26
  • My mistake but I receive the same errors in the afterTextChanged too. – Ozzini Jul 12 '21 at 11:30
  • Change `it` to the argument name that you are receiving in afterTextChanged. As you may know the code written above uses lambda which automatically uses it as the argument name if you only have one. – Rahul Rawat Jul 12 '21 at 11:37
  • As I wrote before I replaced "it" with "result" which is: private lateinit var result: TextInputEditText and then result = binding.editTextInsert but I still get a lot of errors. I don't understand what I'm doing wrong – Ozzini Jul 12 '21 at 11:43
  • Sure. Please check it out – Ozzini Jul 12 '21 at 11:56
  • I said argument of afterTextChanged which is in your case `s` and not result use that – Rahul Rawat Jul 12 '21 at 12:06
  • When I use "s" instead of "result" I got this error: Type mismatch: inferred type is but TextWatcher! was expected. Is it a problem with IDE? – Ozzini Jul 12 '21 at 12:11
  • I don't understand please update the code and also specify the line where the error is coming along with the full stack trace – Rahul Rawat Jul 12 '21 at 12:18
  • I refreshed the project and now with the code I posted above it's running. But when I click buttons there's an event of clicking (I can see and hear) but there's no visible input from this. – Ozzini Jul 12 '21 at 12:27
  • It would be better if you add it as a separate question and not drag this one on. Please accept the answer since it worked. – Rahul Rawat Jul 12 '21 at 12:58
  • It's not working for me. I have transformed the answer from this thread https://stackoverflow.com/a/5233488/14839627 – Ozzini Jul 12 '21 at 14:49
  • I am talking about the edit text part is working. Button click is a different story – Rahul Rawat Jul 12 '21 at 15:20
  • Sorry but your answer isn't correct. I will update the post. Buttons were implemented correctly. – Ozzini Jul 13 '21 at 10:07