Home > database >  How to disable Textwatcher from EditText and enable it for another Edittext?
How to disable Textwatcher from EditText and enable it for another Edittext?

Time:06-06

I am trying to make an App in Kotlin, which converts decimal, binary,base5,base7, and Hexa numbers.

The app has 5 Edittexts and a Textwatcher attached to the first one, when I enter a decimal number in it, it automatically converts the number (using doOnTextChanged) and fills the other EditTexts with their correct values.

But I have a huge problem now. When I try to enter a value in the second EditText my app crashes (Because of conflicting TextWatchers I assume).

Could you please give me an idea, of how I can disable the "doOnTextChanged" from the first EditText and enable it for the others? I would be forever grateful for any piece of Advice!

My Code below:

class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    var decimalnumber: EditText = findViewById(R.id.decimalnr)
    var binarynumber: EditText = findViewById(R.id.binarynumber)
    var base5number: EditText = findViewById(R.id.base5)
    var base7numbeer: EditText = findViewById(R.id.base7)
    var hexanumber: EditText = findViewById(R.id.hexa)

    decimalnumber.setOnFocusChangeListener { v, hasFocus ->

        decimalnumber.doOnTextChanged { text, start, before, count ->
            try {
                var dnumber = decimalnumber.text.toString().toInt()
                if (dnumber > 0) {
                    var binary = Integer.toString(dnumber, 2)
                    var base5nr = Integer.toString(dnumber, 5)
                    var base7nr = Integer.toString(dnumber, 7)
                    var hexanr = Integer.toString(dnumber, 16)

                    binarynumber.setText(binary)
                    base5number.setText(base5nr)
                    base7numbeer.setText(base7nr)
                    hexanumber.setText(hexanr)
                }
            } catch (exception: NumberFormatException) {
                binarynumber.setText("")
                base5number.setText("")
                base7numbeer.setText("")
                hexanumber.setText("")

            }
        }
    }

    binarynumber.setOnFocusChangeListener { v, hasFocus ->

        binarynumber.doOnTextChanged { text, start, before, count ->

            var binnumber = binarynumber.text.toString()
            var dnumber = Integer.valueOf(binnumber, 2)

            var base5 = Integer.toString(dnumber, 5)
            var base7 = Integer.toString(dnumber, 7)
            var hexa = Integer.toString(dnumber, 16)

            binarynumber.setText(binnumber)
            base5number.setText(base5)
            base7numbeer.setText(base7)
            hexanumber.setText(hexa)

        }
    }
}

}

CodePudding user response:

You're setting a TextWatcher on binaryNumber that updates its text every time its text updates:

binarynumber.doOnTextChanged { text, start, before, count ->
    ...
    binarynumber.setText(binnumber)

so you end up with a cycle of the TextWatcher's update callbacks triggering updates that trigger callbacks that trigger updates... You have to be really careful about updating an EditText inside its own TextWatcher for this reason, maybe involving a flag you set while updating, so the callbacks can skip any further updates and let it complete.

It doesn't look like you even need to do it here though? You're just reading the contents of binaryNumber and setting that as its text again. Remove the binarynumber.setText(binnumber) line and it won't trigger the watcher again. But you do have to be careful about situations where a watcher on textA updates textB, which has a watcher that updates textA... same situation, involving multiple watchers. Having an updating boolean they can all see can help:

// a flag visible to all the watchers
var updating = false

textA.doOnTextChanged { text, start, before, count ->
    // if an update is happening, don't go updating anything else!
    if (!updating) {
        // block anything else from forcing an update
        updating = true
        textB.setText("hi)
        // all done
        updating = false
    }
}

if textB checks updating in the same way, once textA does an update, textB will ignore it instead of triggering a response. And then the function in textA can complete and reset updating.


Also as far as I'm aware, that doOnTextChanged ktx extension adds a new TextWatcher every time you call it, so you're piling up mutiple watchers every time something gains or loses focus. Just set them once, during initialisation

  • Related