android on Text Change Listener
Asked Answered
R

19

355

I have a situation, where there are two fields. field1 and field2. All I want to do is empty field2 when field1 is changed and vice versa. So at the end only one field has content on it.

field1 = (EditText)findViewById(R.id.field1);
field2 = (EditText)findViewById(R.id.field2);

field1.addTextChangedListener(new TextWatcher() {

   public void afterTextChanged(Editable s) {}

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

   public void onTextChanged(CharSequence s, int start,
     int before, int count) {
      field2.setText("");
   }
  });

field2.addTextChangedListener(new TextWatcher() {

   public void afterTextChanged(Editable s) {}

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

   public void onTextChanged(CharSequence s, int start,
     int before, int count) {
     field1.setText("");
   }
  });

It works fine if I attach addTextChangedListener to field1 only, but when I do it for both fields the app crashes. Obviously because they try to change each other indefinitely. Once field1 changes it clears field2 at this moment field2 is changed so it will clear field1 and so on...

Can someone suggest any solution?

Rouse answered 29/12, 2013 at 11:23 Comment(1)
to new users, go for two way data-binding using an observable-field of string, cause all the solution provided here may produce starting waiting blocking gc alloc this type of error, which may even lead to crash and hang.. so go for data-binding, that's safe and recommended by google now..Insomnolence
W
560

You can add a check to only clear when the text in the field is not empty (i.e when the length is different than 0).

field1.addTextChangedListener(new TextWatcher() {

   @Override
   public void afterTextChanged(Editable s) {}

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

   @Override    
   public void onTextChanged(CharSequence s, int start,
     int before, int count) {
      if(s.length() != 0)
        field2.setText("");
   }
  });

field2.addTextChangedListener(new TextWatcher() {

   @Override
   public void afterTextChanged(Editable s) {}

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

   @Override
   public void onTextChanged(CharSequence s, int start,
     int before, int count) {
      if(s.length() != 0)
         field1.setText("");
   }
  });

Documentation for TextWatcher here.

Also please respect naming conventions.

Wenda answered 29/12, 2013 at 11:28 Comment(2)
how to detect after all field changed, because, it detect every time it changes, when any button is pressed.Klagenfurt
may i know, what if i were using TextInputLayout?Yakut
B
58

In Kotlin simply use KTX extension function: (It uses TextWatcher)

yourEditText.doOnTextChanged { text, start, count, after -> 
        // action which will be invoked when the text is changing
    }

Same for doAfterTextChanged and doBeforeTextChanged, depending on the functions you want to implement from TextWatcher.


import `core-KTX`:
implementation "androidx.core:core-ktx:1.2.0"
Bladderwort answered 26/2, 2020 at 7:41 Comment(3)
Nice. So much cleanerFarman
last version of Core available at developer.android.com/jetpack/androidx/releases/coreBladderwort
Called when user rotate screen.Rimola
H
26

I know this is old but someone might come across this again someday.

I had a similar problem where I would call setText on a EditText and onTextChanged would be called when I didn't want it to. My first solution was to write some code after calling setText() to undo the damage done by the listener. But that wasn't very elegant. After doing some research and testing I discovered that using getText().clear() clears the text in much the same way as setText(""), but since it isn't setting the text the listener isn't called, so that solved my problem. I switched all my setText("") calls to getText().clear() and I didn't need the bandages anymore, so maybe that will solve your problem too.

Try this:

Field1 = (EditText)findViewById(R.id.field1);
Field2 = (EditText)findViewById(R.id.field2);

Field1.addTextChangedListener(new TextWatcher() {

   public void afterTextChanged(Editable s) {}

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

   public void onTextChanged(CharSequence s, int start,
     int before, int count) {
      Field2.getText().clear();
   }
  });

Field2.addTextChangedListener(new TextWatcher() {

   public void afterTextChanged(Editable s) {}

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

   public void onTextChanged(CharSequence s, int start,
     int before, int count) {
     Field1.getText().clear();
   }
  });
Hypermetropia answered 16/7, 2016 at 2:38 Comment(0)
K
17

If you are using Kotlin for Android development then you can add TextChangedListener() using this code:

myTextField.addTextChangedListener(object : TextWatcher{
        override fun afterTextChanged(s: Editable?) {}

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

        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
    })
Kerrin answered 25/6, 2018 at 7:4 Comment(0)
U
14
var filenameText = findViewById(R.id.filename) as EditText
filenameText.addTextChangedListener(object : TextWatcher {
    override fun afterTextChanged(s: Editable?) {
        filename = filenameText.text.toString()
        Log.i("FileName: ", filename)
    }
    
    override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
    override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
})
Unstoppable answered 3/8, 2020 at 8:21 Comment(0)
M
7

A bit late of a answer, but here is a reusable solution:

/**
 * An extension of TextWatcher which stops further callbacks being called as 
 * a result of a change happening within the callbacks themselves.
 */
public abstract class EditableTextWatcher implements TextWatcher {

    private boolean editing;

    @Override
    public final void beforeTextChanged(CharSequence s, int start, 
                                                    int count, int after) {
        if (editing)
            return;

        editing = true;
        try {
            beforeTextChange(s, start, count, after);
        } finally {
            editing = false;
        }
    }

    protected abstract void beforeTextChange(CharSequence s, int start, 
                                                     int count, int after);

    @Override
    public final void onTextChanged(CharSequence s, int start, 
                                                int before, int count) {
        if (editing)
            return;

        editing = true;
        try {
            onTextChange(s, start, before, count);
        } finally {
            editing = false;
        }
    }

    protected abstract void onTextChange(CharSequence s, int start, 
                                            int before, int count);

    @Override
    public final void afterTextChanged(Editable s) {
        if (editing)
            return;

        editing = true;
        try {
            afterTextChange(s);
        } finally {
            editing = false;
        }
    }

    public boolean isEditing() {
        return editing;
    }

    protected abstract void afterTextChange(Editable s);
}

So when the above is used, any setText() calls happening within the TextWatcher will not result in the TextWatcher being called again:

/**
 * A setText() call in any of the callbacks below will not result in TextWatcher being 
 * called again.
 */
public class MyTextWatcher extends EditableTextWatcher {

    @Override
    protected void beforeTextChange(CharSequence s, int start, int count, int after) {
    }

    @Override
    protected void onTextChange(CharSequence s, int start, int before, int count) {
    }

    @Override
    protected void afterTextChange(Editable s) {
    }
}
Mazy answered 15/4, 2017 at 10:24 Comment(0)
T
6

I have also faced the same problem and keep on getting stackOverflow exceptions, and I come with the following solution.

edt_amnt_sent.addTextChangedListener(new TextWatcher() {    
    @Override
    public void afterTextChanged(Editable s) {
        if (skipOnChange)
            return;

        skipOnChange = true;
        try {
            //method
        } catch (NumberFormatException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            skipOnChange = false;
        }
    }
});

edt_amnt_receive.addTextChangedListener(new TextWatcher() {

    @Override
    public void afterTextChanged(Editable s) {

        if (skipOnChange)
            return;

        skipOnChange = true;
        try {
            //method
        } catch (NumberFormatException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            skipOnChange = false;
        }
    }
});

declared initially boolean skipOnChange = false;

Trent answered 14/12, 2015 at 7:59 Comment(1)
"stack full" I think you mean Stack Overflow ;)Glut
I
6

I wrote my own extension for this, very helpful for me. (Kotlin)

You can write only like that :

editText.customAfterTextChanged { editable -> 
    //You have accessed the editable object. 
}

My extension :

fun EditText.customAfterTextChanged(action: (Editable?)-> Unit){
    this.addTextChangedListener(object : TextWatcher {
       override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
       override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
       override fun afterTextChanged(editable: Editable?) {
        action(editable)
    }
})}
Inclinable answered 25/9, 2019 at 12:42 Comment(4)
Thanks for this. However I don't understand Kotlin syntax in this scenario. Could you walk me through the syntax of the "editable ->" thing, as well as how one can globally override a field of EditText that doesn't affect all EditTexts in other Activities?Hanyang
Sorry @Hanyang I couldn't understand. What do you want to do?Inclinable
I just want to understand your code above.Hanyang
I added a text change listener to the edittext. This listener returns a value every time a change is made with the "action" function added as a parameter.Inclinable
P
5

You can also use the hasFocus() method:

public void onTextChanged(CharSequence s, int start,
     int before, int count) {
     if (Field2.hasfocus()){
         Field1.setText("");
     }
   }

Tested this for a college assignment I was working on to convert temperature scales as the user typed them in. Worked perfectly, and it's way simpler.

Posset answered 10/3, 2016 at 3:58 Comment(2)
What about editText.setText when user inputs in it? EditText has focus in this caseCalaverite
best solution .Willamina
C
3

check String before set another EditText to empty. if Field1 is empty then why need to change again to ( "" )? so you can check the size of Your String with s.lenght() or any other solution

another way that you can check lenght of String is:

String sUsername = Field1.getText().toString();
if (!sUsername.matches(""))
{
// do your job
}
Collocutor answered 29/12, 2013 at 11:28 Comment(0)
S
3
editText.addTextChangedListener(new TextWatcher() 
{
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) 
            {
            }

            @Override
            public void onTextChanged(CharSequence chr, int i, int i1, int i2) 
            {
                //Check char sequence is empty or not
                if (chr.length() > 0)
                {
                    //Your Code Here
                }
            }

            @Override
            public void afterTextChanged(Editable editable) 
            {
            }
 });
Stutsman answered 28/12, 2022 at 7:42 Comment(0)
G
1
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) {
                if (noteid != -1) {
                    MainActivity.notes.set(noteid, String.valueOf(charSequence));
                    MainActivity.arrayAdapter.notifyDataSetChanged();
                }
            }
            @Override
            public void afterTextChanged(Editable editable) {

            }
        });

in this code noteid is basically arguments taken back which is being putted into the indent or passed through the indent.

  Intent intent = getIntent();
         noteid = intent.getIntExtra("noteid", -1);

the code on the downside is basically the extra code ,if you want to understand more clearly.

how to make the menu or insert the menu in our code , 
    create the  menu folder this the folder created by going into the raw
    ->rightclick->
    directory->name the folder as you wish->
    then click on the directory formed->
    then click on new file and then name for file as you wish ie the folder name file
    and now type the 2 lines code in it and see the magic.

new activity code named as NoteEditor.java for editing purpose,my app is basicley the note app.

package com.example.elavi.notes;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.EditText;
import android.widget.Toast;

import static android.media.CamcorderProfile.get;
public class NoteEditorActivity extends AppCompatActivity {
    EditText editText;
    int noteid;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_note_editor);
        editText = findViewById(R.id.editText);
        Intent intent = getIntent();
         noteid = intent.getIntExtra("noteid", -1);
        if (noteid != -1) {
            String text = MainActivity.notes.get(noteid);
            editText.setText(text);

           Toast.makeText(getApplicationContext(),"The arraylist content is"+MainActivity.notes.get(noteid),Toast.LENGTH_SHORT).show();
        }
        else
        {
            Toast.makeText(getApplicationContext(),"Here we go",Toast.LENGTH_SHORT).show();
            MainActivity.notes.add("");
            noteid=MainActivity.notes.size()-1;
        }
        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) {
                if (noteid != -1) {
                    MainActivity.notes.set(noteid, String.valueOf(charSequence));
                    MainActivity.arrayAdapter.notifyDataSetChanged();
                }
            }
            @Override
            public void afterTextChanged(Editable editable) {

            }
        });
    }
}
Goatfish answered 2/1, 2020 at 14:55 Comment(0)
P
1

We can remove the TextWatcher for a field just before editing its text then add it back after editing the text.

Declare Text Watchers for both field1 and field2 as separate variables to give them a name: e.g. for field1

private TextWatcher Field_1_Watcher = new TextWatcher() {
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    }

    @Override
    public void afterTextChanged(Editable s) {
    }

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

    }

};

then add the watcher using its name: field1.addTextChangedListener(Field_1_Watcher) for field1, and field2.addTextChangedListener(Field_2_Watcher) for field2

Before changing the field2 text remove the TextWatcher: field2.removeTextChangedListener(Field_2_Watcher) change the text: field2.setText("")

then add the TextWatcher back: field2.addTextChangedListener(Field_2_Watcher)

Do the same for the other field

Persistent answered 15/4, 2020 at 16:35 Comment(0)
P
1

Another solution that may help someone. There are 2 EditText which change instead of each other after editing. By default, it led to cyclicity.

use variable:

Boolean uahEdited = false;
Boolean usdEdited = false;

add TextWatcher

uahEdit = findViewById(R.id.uahEdit);
usdEdit = findViewById(R.id.usdEdit);

uahEdit.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            if (!usdEdited) {
                uahEdited = true;
            }
        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            String tmp = uahEdit.getText().toString();

            if(!tmp.isEmpty() && uahEdited) {
                uah = Double.valueOf(tmp);
                usd = uah / 27;
                usdEdit.setText(String.valueOf(usd));
            } else if (tmp.isEmpty()) {
                usdEdit.getText().clear();
            }
        }

        @Override
        public void afterTextChanged(Editable s) {
            uahEdited = false;
        }
    });

usdEdit.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            if (!uahEdited) {
                usdEdited = true;
            }
        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            String tmp = usdEdit.getText().toString();

            if (!tmp.isEmpty() && usdEdited) {
                usd = Double.valueOf(tmp);
                uah = usd * 27;
                uahEdit.setText(String.valueOf(uah));
            } else if (tmp.isEmpty()) {
                uahEdit.getText().clear();
            }
        }

        @Override
        public void afterTextChanged(Editable s) {
            usdEdited = false;
        }
    });

Don't criticize too much. I am a novice developer

Parament answered 26/6, 2020 at 19:0 Comment(0)
T
1
etSearch.addTextChangedListener(object : TextWatcher {
     override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
          TODO("Not yet implemented")
     }

     override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
          TODO("Not yet implemented")
     }

     override fun afterTextChanged(p0: Editable?) {
          TODO("Not yet implemented")
     }
})
Tijerina answered 23/9, 2022 at 17:15 Comment(0)
E
0

you use this
I have provided latest method for textchangelistner

edMsg.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) {
                
                if (i2 == 0){
                    ////Edit text blanked
                } 
                
                String msg = charSequence.toString();/// your text on changed in edit text
                
            }

            @Override
            public void afterTextChanged(Editable editable) {

            }
        });
Endoergic answered 14/12, 2022 at 11:48 Comment(0)
C
0

This worked for me.

Make the reference to edittext, add the method addtextchangedlistener. Then add the variable isEdit that will help to execute only 1 time any of the events beforetextchagned, ontextchagned o aftertextchagned. Inside of the try you can implement your logic or whatever you need, even reassign the value of the edittext as in my case

numberFloat.addTextChangedListener(object : TextWatcher{
   private var isEdit:Boolean = false
   override fun beforeTextChanged(text: CharSequence?, p1: Int, p2: Int, p3: Int){
       if(isEdit)
         return
       isEdit = true
       try {
          //tus condiciones, logica o evento
       }finally {
          isEdit = false
       }
   }
   override fun onTextChanged(text: CharSequence?, p1: Int, p2: Int, p3: Int){
       if(isEdit)
         return
       isEdit = true
       try {
         //tus condiciones, logica, evento, reasignar nuevo valor al editText
         if(!text.isNullOrEmpty()){
             if(text.length > 3){
               println("es mayor a 3")
               numberFloat.setText("0")
             }
         }
       }finally {
          isEdit = false
       }
   }
   override fun afterTextChanged(text: Editable?) {
       if(isEdit)
         return
       isEdit = true
       try {
          //tus condiciones, logica o evento
       }finally {
          isEdit = false
       }
   }
})
Calceolaria answered 1/5, 2023 at 22:31 Comment(0)
J
0

If you don't want to override all methods.

fun EditText.afterTextChanged(afterTextChanged: (chars: Editable?) -> Unit = { _ -> }) {
    addTextChangedListener(object : TextWatcher {
        override fun afterTextChanged(s: Editable?) {
            afterTextChanged(s)
        }

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

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

    })
}

fun EditText.beforeTextChanged(beforeTextChanged: (chars: CharSequence?, start: Int, count: Int, after: Int) -> Unit = { _, _, _, _ -> }) {
    addTextChangedListener(object : TextWatcher {
        override fun afterTextChanged(s: Editable?) {
        }

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

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

    })
}

fun EditText.onTextChanged(onTextChanged: (chars: CharSequence?, start: Int, count: Int, after: Int) -> Unit = { _, _, _, _ -> }) {
    addTextChangedListener(object : TextWatcher {
        override fun afterTextChanged(s: Editable?) {
        }

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

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

    })
}
Jacobinism answered 22/10, 2023 at 15:24 Comment(0)
B
-4

Add background dynamically in onCreate method:

getWindow().setBackgroundDrawableResource(R.drawable.background);

also remove background from XML.

Bridwell answered 16/12, 2019 at 10:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.