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