Mohammad Ersan
Mohammad Ersan

Reputation: 12444

How to change TextView text on DataChange without calling back a TextWatcher listener

Consider:

TextView textView = new TextView(context);
    textView.addTextChangedListener(new TextWatcher() {

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count,
                                      int after) {
        }

        @Override
        public void afterTextChanged(Editable s) {

            s.append("A");
        }
    });

If we add a TextWatcher to a TextView, and I want to append a letter to this TextView, every time the user writes a letter in it, but this keeps calling the TextWatcher Listener, so on to StackOverFlow error, so how can I append text without calling the TextWatcher Listener again?

Upvotes: 11

Views: 14420

Answers (5)

j2emanue
j2emanue

Reputation: 62519

Some pseudocode so you can do this:

Just change the focus...

So like this:

tv.isFocusable = false

tv.setText("my new text")

tv.isFocusable = true // Maybe post this to the message queue, so other jobs finish fist.


// Later on in your listener:

if(tv.isFocusable && tv.hasFocus())
    // Do something
else ignore

Upvotes: 0

Yuriy Vasylenko
Yuriy Vasylenko

Reputation: 3151

Another way to avoid a stack overflow:

TextView textView = new TextView(context);
    textView.addTextChangedListener(new TextWatcher() {

        boolean editing = false;

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {

        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count,int after) {

        }

        @Override
        public void afterTextChanged(Editable s) {
            if (!editing){
               editing = true;
               s.append("A");
               editing = false;
        }

    }
});

Upvotes: 8

Kavin Varnan
Kavin Varnan

Reputation: 1989

Kotlin Version

editText.addTextChangedListener(object: TextWatcher {
    override fun afterTextChanged(s: Editable?) {
        if (s.toString().isNotBlank()) {

            val formattedValue: String = // Do some formatting

            editText.removeTextChangedListener(this)
            editText.setText(formattedValue)
            editText.setSelection(editText.text.toString().length)
            editText.addTextChangedListener(this)
        }
    }

    override fun beforeTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { }

    override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {

    }

})

Upvotes: -2

Andrii Chernenko
Andrii Chernenko

Reputation: 10194

It's easy:

@Override
public void afterTextChanged(Editable s) {
    editText.removeTextChangedListener(this);
    //Any modifications at this point will not be detected by TextWatcher,
    //so no more StackOverflowError 
    s.append("A");
    editText.addTextChangedListener(this);
}

Upvotes: 32

iDroid
iDroid

Reputation: 10533

The documentation of afterTextChanged says:

This method is called to notify you that, somewhere within s, the text has been changed. It is legitimate to make further changes to s from this callback, but be careful not to get yourself into an infinite loop, because any changes you make will cause this method to be called again recursively. (You are not told where the change took place because other afterTextChanged() methods may already have made other changes and invalidated the offsets. But if you need to know here, you can use setSpan(Object, int, int, int) in onTextChanged(CharSequence, int, int, int) to mark your place and then look up from here where the span ended up.

So, with every s.append("A") you call afterTextChanged() again and so on.

Upvotes: 5

Related Questions