6

I've been trying to get specific behaviour with EditText. This is my simple activity with rotation enabled. When I enter some text, onTextChanged method is triggered.

When I rotate the phone, this method should not be triggered, bacause the text, which was saved in onInstanceState is already set into EditText in onCreate method.

However, when I rotate the phone back to its original orientation, method triggers again! See logcat at the end of this post.

This is a copy and paste code from a newly created Android Studio project.

public class MainActivity extends AppCompatActivity {

    EditText editText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        editText = (EditText) findViewById(R.id.search_query);
    }

    @Override
    protected void onPostCreate(@Nullable Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        editText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

            }

            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                Log.d("MainActivity", "charSequence:" + charSequence);
            }

            @Override
            public void afterTextChanged(Editable editable) {

            }
        });
    }
}

UPD:

Android retains text in all editText fields (with unique ID) on each orientation change by default. That is why onTextChanged is triggered when I rotate my phone. To avoid this I placed onTextChanged listener after onCreate, so it will listen events after text is set to EditText. But it does NOT WORK as expected(logcat). Check the last link for more info

logcat:

 D/MainActivity: onCreate
 D/MainActivity: charSequence:a //entered manually
 D/MainActivity: charSequence:a // triggered on orientation change
 D/MainActivity: onDestroy
 D/MainActivity: onCreate
 D/MainActivity: onDestroy
 D/MainActivity: onCreate
 D/MainActivity: onDestroy
 D/MainActivity: onCreate
 D/MainActivity: onDestroy // no callback so far
 D/MainActivity: onCreate
 D/MainActivity: charSequence:ba // entered manyally
 D/MainActivity: onDestroy
 D/MainActivity: onCreate
 D/MainActivity: charSequence:ba // when came back to original "ba" orientation
 D/MainActivity: onDestroy
 D/MainActivity: onCreate

Relevant: TextWatcher called even if text is set before adding the watcher

Android retain callback state after configuration change

nutella_eater
  • 3,393
  • 3
  • 27
  • 46
  • "... the text, which was saved onInstanceState is already set into EditText in onCreate method" I don't see it in your code. – Willi Mentzel Jul 11 '17 at 13:34
  • @WilliMentzel it is set by android system. I think that property called freezesText. Check the second link – nutella_eater Jul 11 '17 at 13:36
  • Please see updated answer. – Andrii Omelchenko Jul 11 '17 at 14:15
  • @alexeypolusov the EditText should retain its value with or without retrieving it explicitely from the saveInstanceState. If I run the code exaclty as provided by you, onTextChanged is not triggered when I rotate and the value of the EditText remains the same (even after rotation). – Willi Mentzel Jul 11 '17 at 19:32
  • @alexeypolusov are you using exaclty that code, when encountering the problem? – Willi Mentzel Jul 11 '17 at 20:22
  • @WilliMentzel - yes I do. I expected that onTextChanged should not be triggered, but I triggers in some random places which is reflected in the logcat I've provided. Run it on your machine and see for yourself – nutella_eater Jul 12 '17 at 06:23
  • 1
    @alexeypolusov that is the problem, for me it does not happen. API version? Emulator? – Willi Mentzel Jul 12 '17 at 07:25
  • @WilliMentzel, TRUE. That's a phone related bug. Nexus 5x and others don't have this issue. The culprit is Motorola xt 1052. Thanks – nutella_eater Jul 12 '17 at 07:40

4 Answers4

1

Take a look at this acticle, especially at:

You can notice that the text in the first EditText (with ID) will keep no change after orientation changed, and onTextChanged() method of the TextChangedListener will be called also, to update the TextView.

On the other hand, the second EditText (without ID assigned) will clear to empty when orientation changed.

Anyway, You can suppress onTextChanged() like in this answer of athor:

...you can get around it by either:

  • Removing the TextWatcher before you set the text, and adding it back after.
  • Or set a boolean flag before you set the text, which tells the TextWatcher to ignore it.

E.g.

boolean ignoreNextTextChange = true; ((EditText)
findViewById(R.id.MyEditText)).setText("Hello");

And in your TextWatcher:

new TextWatcher() {
     @Override
     public void onTextChanged(CharSequence s, int start, int before, int count) {
         /*
         If the flag is set to ignore the next text change,
         reset the flag, and return without doing anything.
         */
         if (ignoreNextTextChange){
             ignoreNextTextChange = false;
             return;
         }
     }

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

     }

     @Override
     public void afterTextChanged(Editable s) {

     } });
Andrii Omelchenko
  • 13,183
  • 12
  • 43
  • 79
  • I just don't see why this should't work out of box as I expect it to be. Sure, I can avoid this quirk, but I want to avoid states and "ifs" as much as possible to make my code clear. – nutella_eater Jul 11 '17 at 13:47
  • @AndriiOmelchenko You should post this as a comment. Quoting another answer without adding anything new to it is not encouraged. Image the original answre changes and your quoted answer remains the same. – Willi Mentzel Jul 11 '17 at 13:55
  • @WilliMentzel I'm update answer and add something new. – Andrii Omelchenko Jul 11 '17 at 14:16
0

If you have saved the entered text in savedInstanceState then just check if you have saved variable and based on that condition fetch the value from it and set it to edittext. No matter how many times some method is called. :D Problem solved.

Balvir Jha
  • 47
  • 2
0

So, the problem was in the device. Other devices such as Nexus 5X does not show any issues and code works as expected.

nutella_eater
  • 3,393
  • 3
  • 27
  • 46
0

My problem was inside Fragment (I know, it's not Activity, but maybe it will be useful).

After phone rotation got fake TextWatcher call. Text changes from empty to previous one (before rotation).

I migrate all TextWatcher setup from onViewCreated to onViewStateRestored. Because in last one happens restoring of your EditText text. Comment from my code:

/**
 * Make all setupView staff here, for prevent [TextWatcher]'s false call.
 *
 * Bad case:
 * 1. [onViewCreated] = [EditText] is empty
 * 2. [setupView] = add [TextWatcher]
 * 3. [onViewStateRestored] = [EditText] restore text and it calls [TextWatcher]
 * You got text change from '' to 'your text'.
 *
 * Good case:
 * 1. [onViewCreated] = [EditText] is empty
 * 2. [onViewStateRestored] = [EditText] restore text
 * 3. [setupView] = add [TextWatcher]
 * You didn't get any text changes while [BindingFragment] initialization.
 */
override fun onViewStateRestored(savedInstanceState: Bundle?) {
    super.onViewStateRestored(savedInstanceState)

    ...
}

P.S.

  1. Don't forget to set id for your EditText, it's needed for view state restoring.
  2. Check lifecycle guide, for better understanding.
SerjantArbuz
  • 982
  • 1
  • 12
  • 16