My solution for this is a lot like the others only it's my custom spin on it using viewbindings
I created the following TextWatcher
class ControlledTextWatcher(
private val parent: TextView,
private val onChange: ((text: CharSequence?, start: Int, before: Int, count: Int) -> Unit)?,
private val beforeChange: ((text: CharSequence?, start: Int, count: Int, after: Int) -> Unit)? = null,
private val afterChange: ((editable: Editable?) -> Unit)? = null
) : TextWatcher {
init {
parent.addTextChangedListener(this)
}
private var enabled = true
var text: String?
get() = parent.value
set(value) {
this.enabled = false
parent.text = value
this.enabled = true
}
var res: Int
get() = throw RuntimeException("String resource cannot be retrieved after being set")
set(value) {
parent.text = parent.context.getString(value)
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
if (enabled)
beforeChange?.invoke(s, start, count, after)
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
if (enabled)
onChange?.invoke(s, start, before, count)
}
override fun afterTextChanged(s: Editable?) {
if (enabled)
afterChange?.invoke(s)
}
fun detach() {
parent.removeTextChangedListener(this)
}
}
and I use it mainly with view bindings like so
class TestActivity : AppCompatActivity() {
class TestActivity : AppCompatActivity() {
private lateinit var binding: ActivityTestBinding
private val edit by lazy { ControlledTextWatcher(binding.text, this::textChanged }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityTestBinding.inflate(layoutInflater)
setContentView(binding.root)
}
so when I wish to make changes to the actual EditText I use the text or res attribute of the ControlledTextWatcher like so:
edit.text = "hello world" //this does not trigger the text watcher
but when the user alters the EditText it will trigger
unfortunatelly with this solution if you want to alter other parameters of the EditText, you either have to get the original EditText through bindings or copy those functions to the ControlledTextWatcher
also you have to be careful when making changes in afterChange because the change is posted to the TextView so you may end up with an endless loop