73

I want to implement the TextWatcher interface for more than one EditText fields. Currently I am using :

text1.addTextChangedListener(this);
text2.addTextChangedListener(this);

then overriding the methods in my Activity:

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) 
{
 // do some operation on text of text1 field
 // do some operation on text of text2 field 
}

However this is working fine but I'm looking for other ways so that I can explicitly identify that in which EditText field the SoftKeyboard is currently focused.

Bobs
  • 21,870
  • 33
  • 134
  • 221
Vikas Patidar
  • 41,230
  • 22
  • 89
  • 104
  • 1
    Possible duplicate of [How to use Single TextWatcher for multiple EditTexts?](http://stackoverflow.com/questions/5702771/how-to-use-single-textwatcher-for-multiple-edittexts) – Sky Kelsey Oct 27 '15 at 04:04
  • 1
    @SkyKelsey The answer you wrote is actually creating multiple instances of TextWatcher, therefore I think that is not the perfect answer for what has been asked by OP, regarding my question is about identifying the each EditText in a TextWatcher interface methods. – Vikas Patidar Oct 29 '15 at 12:48
  • https://github.com/henrychuangtw/AutoInsertEditText – HenryChuang Jan 18 '17 at 02:12

16 Answers16

96

Suggested solution in @Sebastian Roth's answer is not one instance of TextWatcher for some EditTexts. It is one class and n instances of that class for n EditTexts.

Each EditText has its own Spannable. TextWatcher's events has this Spannable as s parameter. I check their hashCode (unique Id of each object). myEditText1.getText() returns that Spannable. So if the myEditText1.getText().hashCode() equals with s.hashCode() it means that s belongs to myEditText1

So if you want to have one instance of TextWatcher for some EditTexts you should use this:

private TextWatcher generalTextWatcher = new TextWatcher() {    

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

        if (myEditText1.getText().hashCode() == s.hashCode())
        {
            myEditText1_onTextChanged(s, start, before, count);
        }
        else if (myEditText2.getText().hashCode() == s.hashCode())
        {
            myEditText2_onTextChanged(s, start, before, count);
        }
    }

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

        if (myEditText1.getText().hashCode() == s.hashCode())
        {
            myEditText1_beforeTextChanged(s, start, count, after);
        }
        else if (myEditText2.getText().hashCode() == s.hashCode())
        {
            myEditText2_beforeTextChanged(s, start, count, after);
        }
    }

    @Override
    public void afterTextChanged(Editable s) {
        if (myEditText1.getText().hashCode() == s.hashCode())
        {
            myEditText1_afterTextChanged(s);
        }
        else if (myEditText2.getText().hashCode() == s.hashCode())
        {
            myEditText2_afterTextChanged(s);
        }
    }

};

and

myEditText1.addTextChangedListener(generalTextWatcher);
myEditText2.addTextChangedListener(generalTextWatcher);
Community
  • 1
  • 1
Bobs
  • 21,870
  • 33
  • 134
  • 221
  • 3
    Have you double checked this solution? What happens if a user enters the same text in both input fields? – Sebastian Roth Dec 10 '12 at 02:11
  • 15
    @SebastianRoth Each EditText has its own Spannable. `TextWatcher` events has this Spannable as `CharSequence` or `Editable` parameters. I do not check the Text value of EditTexts. I check their hashCode (unique Id of each object). `myEditText1.getText()` returns that `Spannable` not the Text value. So if the `myEditText1.getText().hashCode()` equals with `s.hashCode()` means that s belongs to `myEditText1` – Bobs Dec 10 '12 at 05:57
  • Your opening paragraph is confusing - at least it confused me. "Suggested solution is ... ". I thought you were saying that is what you were suggesting. – RenniePet Sep 21 '14 at 11:18
  • @RenniePet I mean Sebastian Roth's answer – Bobs Sep 21 '14 at 12:31
  • 1
    OK, thanks for the edit. But now I have another question. (I'm fairly new to Android and Java, so please bear with me.) I don't understand why you are involving the hashCode() method to test if the spannable objects are the same objects or not. Why not just compare them with "=="? (In C# I'd use ObjectEquals(), but in Java one just uses ==, right?) – RenniePet Sep 21 '14 at 12:38
  • 1
    This is a bad solution. As soon as you have two EditTexts with the same string it will fail as it will be the same string (therefore the same hashcode) in memory. – Eurig Jones Mar 25 '15 at 16:51
  • @Amorgos `myEditText1.getText().hashCode() == s.hashCode()` does not check the content of the Editable. I check the Editable objects which is different for each EditText and same for every content – Bobs Mar 28 '15 at 05:25
  • 1
    Are you sure all hashes are unique? What about hash collisions? – miva2 Sep 14 '15 at 08:19
  • @breceivemail, what is required to do in methods you are calling in all if else statements? e.g. `myEditText1_onTextChanged(s, start, before, count);` and others more. I am implementing one textwatcher instance and i want to use it for multiple edittext which they all share same input. – Krups May 07 '16 at 08:24
  • 1
    So far best solution. – Sam Chen Feb 18 '20 at 16:01
  • It's just bad and inconsistent library design. I mean why doesn't the `onTextChanged` also pass the sender object same as `onFocusChange`?! That would make everything a lot easier. – BdR Mar 27 '20 at 23:13
88

I would do it like this:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    EditText e = new EditText(this);
    e.addTextChangedListener(new CustomTextWatcher(e));
}

private class CustomTextWatcher implements TextWatcher {
    private EditText mEditText;

    public CustomTextWatcher(EditText e) { 
        mEditText = e;
    }

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

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

    public void afterTextChanged(Editable s) {
    }
}
Bobs
  • 21,870
  • 33
  • 134
  • 221
Sebastian Roth
  • 10,454
  • 13
  • 56
  • 103
  • 6
    Interesting idea, but this means that you have a separate text watcher for each EditText. – ArtOfWarfare Dec 03 '12 at 19:41
  • 13
    this answer is not `Single TextWatcher for multiple EditTexts`. It is 3 instances of one TextWatcher class. So 3 separate TextWatchers are controlling 3 EditTexts. – Bobs Dec 08 '12 at 12:07
  • 1
    @breceivemail, one class, 3 instances. Agreed it's hard when people don't specify their object type. His objective was to write the code only once, so there it is. – SparK Oct 24 '13 at 13:23
  • What's so custom about this ? I don't get it – r3dm4n Feb 25 '19 at 08:20
  • @Bobs, I suppose it is easier to make 3 objects instead of writing one difficult and support it's future changes. – CoolMind Mar 15 '19 at 13:36
13

I use this solution:

  • Add method that returns listener:

    private TextWatcher getTextWatcher(final EditText editText) {
        return 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) {
                // do what you want with your EditText
                editText.setText("blabla");
            }
    
            @Override
            public void afterTextChanged(Editable editable) {
    
            }
        };
    }
    
  • Add listener to multiple EditText's, you can also pass other parameters:

    editText1.addTextChangedListener(getTextWatcher(editText1));
    editText2.addTextChangedListener(getTextWatcher(editText2));
    editText3.addTextChangedListener(getTextWatcher(editText3));
    
Wojtek
  • 990
  • 2
  • 15
  • 21
12

using "CustomTextWatcher" idea, I done that

1) Crated a new TextWatcherListener interface:

public interface TextWatcherExtendedListener extends NoCopySpan
{
    public void afterTextChanged(View v, Editable s);

    public void onTextChanged(View v, CharSequence s, int start, int before, int count);

    public void beforeTextChanged(View v, CharSequence s, int start, int count, int after);
}

2)Created and used EditTextExtended instead of EditText (in my case):

public class EditTextExtended extends EditText
{
   private TextWatcherExtendedListener  mListeners = null;

   public EditTextExtended(Context context) 
   {
     super(context);
   }

   public EditTextExtended(Context context, AttributeSet attrs)
   {
      super(context, attrs);
   }

   public EditTextExtended(Context context, AttributeSet attrs, int defStyle)
   {
        super(context, attrs, defStyle);
   }

   public void addTextChangedListener(TextWatcherExtendedListener watcher) 
   {    
       if (mListeners == null) 
       {
           mListeners = watcher;
       }
   }

   public void removeTextChangedListener(TextWatcherExtendedListener watcher) 
   {
       if (mListeners != null) 
       {
           mListeners = null;        
       }
   }

   void  sendBeforeTextChanged(CharSequence text, int start, int before, int after)
   {
       if (mListeners != null) 
       {
           mListeners.beforeTextChanged(this, text, start, before, after);
       }
   }

   void  sendOnTextChanged(CharSequence text, int start, int before,int after) 
   {
       if (mListeners != null) 
       {
           mListeners.onTextChanged(this, text, start, before, after);
       }
   }

   void  sendAfterTextChanged(Editable text) 
   {
       if (mListeners != null)
       {
           mListeners.afterTextChanged(this, text);
       }
   }
}

3) So, where you need write this code:

myEditTextExtended.addTextChangedListener(this) //Let implement TextWatcherExtendedListener methods

4)use them:

@Override
public void onTextChanged(View v, CharSequence s, int start, int before, int count) 
{
   //Tested and works
   //do your stuff  
}


@Override
public void beforeTextChanged(View v, CharSequence s, int start, int count, int after)
{   
     //not yet tested but it should work    
}

@Override
public void afterTextChanged(View v, Editable s) 
{
    //not yet tested but it should work 
}

Well, let me know what do you think.

user3132789
  • 121
  • 1
  • 3
12

--EDIT--

If you want to use only afterTextChanged compare editables:

@Override
public void afterTextChanged(Editable editable) {
    if (editable == mEditText1.getEditableText()) {
        // DO STH
    } else if (editable == mEditText2.getEditableText()) {
        // DO STH
    }
}
Tomasz
  • 1,346
  • 1
  • 17
  • 26
  • 1
    Doesn't this assume there will never be the same text in multiple EditText views? Depending on the app that may not be a valid assumption. – RenniePet Sep 20 '14 at 23:09
  • Damn, I'm very sorry, but I am now convinced that I was wrong. I now think that both your original version and the edited version are correct - you are comparing the Java object returned by EditText.getEditableText() with the provided Editable-implementing object, not the texts involved. (In my testing the two objects seem to always be a SpannableStringBuilder object, and Google's documentation for SpannableStringBuilder.equals() says clearly that it compares objects, not the contained text.) – RenniePet Sep 23 '14 at 13:57
  • @RenniePet The first version of my answer was wrong. We shouldn't compare values of the edit texts. Now it works great. I see that my answer should be somewhere on the top of this thread. – Tomasz Sep 23 '14 at 14:08
  • For some reason, this is working in my emulator, but not in the real device, Finally, this answer worked for me: https://stackoverflow.com/a/13787221/2263329 Comparing `hashCode()` of the `Editable`. – Mahmudul Hasan Shohag Jun 07 '20 at 15:54
2

One more way around is to add OnClickListener to EditText and set a global variable as given below

EditText etCurrentEditor;//Global variable

@Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
        if(v instanceof EditText){
            etCurrentEditor=(EditText)v;
        }
    }

Use this etCurrentEditor as a reference to currently edited EditText

@Override
    public void afterTextChanged(Editable editable) {
        // TODO Auto-generated method stub
        switch (etCurrentEditor.getId()) {
        case R.id.EDITTEXTID:
            break;
        default:
            break;
        }
    }
2

I implemented it as:

edittext1.addTextChangedListener(this);
edittext2.addTextChangedListener(this);
edittext3.addTextChangedListener(this);

and:

@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(edittext1.hasFocus()){
        //text changed for edittext1
    }else if(edittext2.hasFocus()){
        //text changed for edittext2
    }else {
        //text changed for edittext3
    }
}

@Override
public void afterTextChanged(Editable editable) {

}
Lakhan Sharma
  • 1,721
  • 1
  • 11
  • 19
2

After try several ways to achieve this, i find the right way using EditText.isFocused() to distinguish one to another. For example:

    private class OnTextChangedListener implements 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) {

    }

    @Override
    public void afterTextChanged(Editable editable) {
        if (edtName.isFocused()) {
            //do something
        } else if (edtEmail.isFocused()) {
            //do something
        } else if (edtContent.isFocused()) {
             //do something
        }
    }
}
Hien Quang Le
  • 160
  • 1
  • 5
1

Yes, you could use multiple instances of a custom TextWatcher that store the TextView. (TextView is actually the class that has addTextChangedListener.)

Similar to the hashCode solution above you can just check if getText()==s. Instead of either storing all your controls or findViewById multiple times, you could simply scan the content tree yourself once for the control that has the CharSequence.

public TextView findTextView(View v, CharSequence s)
{
   TextView tv;
   ViewGroup vg;
   int i, n;

   if (v instanceof TextView)
   {
      tv = (TextView) v;
      if (tv.getText()==s) return(tv);
   }

   else if (v instanceof ViewGroup)
   {
      vg = (ViewGroup) v;
      n = vg.getChildCount();
      for(i=0;i<n;i++)
      {
         tv = findTextView(vg.getChildAt(i), s);
         if (tv!=null) return(tv);
      }
   }

   return(null);
}

public void afterTextChanged(Editable s)
{
   TextView tv=findTextView(findViewById(android.R.id.content), s);
   if (tv==null) return;
   switch(tv.getId())
   {
      case R.id.path:
         break;
      case  R.id.title:
         break;
   }
}

Of course you could also use findTextView inside beforeTextChanged and onTextChanged.

Renate
  • 651
  • 3
  • 9
  • Wonderful solution. It can identify the EditText which I'm editing when used within TextWatcher overridden methods. Can you explain a little bit about how did you get to this solution and used `findViewById(android.R.id.content) to get the editing view? – Randika Vishman Aug 30 '16 at 06:42
  • `findViewById(android.R.id.content)` is a bit overkill, it gets you the `ViewGroup` which holds your content. It's better to pick the lowest down `ViewGroup` which holds all the text-changing things of interest. – Renate Sep 01 '16 at 01:46
1

Global One class for all the activities.

CustomTextWatcher.java

package org.logicbridge.freshclub.customizedItems;

import android.content.Context;
import android.text.Editable;
import android.text.TextWatcher;
    public class CustomTextWatcher implements TextWatcher {
        private EditText mEditText;
        Context context;

        public CustomTextWatcher(EditText e, Context context) {
            mEditText = e;
            this.context = context;
        }

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

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

        public void afterTextChanged(Editable s) {

        }
    }
Dharmik
  • 2,235
  • 2
  • 24
  • 35
1

You can do this for getting the id of the edit texts. It's not tested but let me know if it works.

//setting textWatcher for the editText
textWatcher(owner_name);


public void textWatcher(final EditText editText){

    TextWatcher watcher = new TextWatcher() {
        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {

           if(editText.getId()==R.id.your_id){
             //Do something
           }   
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
          if(editText.getId()==R.id.your_id){
          //Do something
          }
        }

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

    editText.addTextChangedListener(watcher);
}
OhhhThatVarun
  • 2,305
  • 1
  • 15
  • 33
0

You can always define TextWatcher as a parameter to addTextChangedListener method.This way you can have multiple definitions for each edit text.

vITs
  • 1,563
  • 10
  • 27
www
  • 1
  • 1
0

just compare hash codes of the edittext and string like by using hashCode() method

@Override
public void afterTextChanged(Editable s) {

    if (editext.getText().hashCode() == s.hashCode()){
        type1Total(type1List);
    }

}
Sai Gopi Me
  • 9,800
  • 1
  • 65
  • 43
0

This is what I have done...

private TextWatcher textWatcher = new TextWatcher() {
    @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 (editText1.getText().length() > 0
                && editText2.getText().length() > 0
                && editText3.getText().length() > 0) {

            button.setEnabled(true);
        } else {

            button.setEnabled(false);
        }

    }

    @Override
    public void afterTextChanged(Editable s) {

    }

Then just added the TextWatcher to each EditText in the onCreate method & also kept the button setEnabled(false) by default here.

button.setEnabled(false); 

    editText1.addTextChangedListener(textWatcher);
    editText2.addTextChangedListener(textWatcher);
    editText3.addTextChangedListener(textWatcher);
0

If you are using Kotlin, extension function will do the job. For example, we need to add TextWatcher to editText1 and editText2

Create a extension function like this,

 fun EditText.addTextWatcher() {
        this.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) {
                        // you do some common operations here

                        when (this@addTextWatcher) {
                            editText1 -> {
                                // do something for editText1
                            }
                            editText2 -> {
                                // do something for editText2
                            }
                        }
                    }
                }
        )
    }

Then just add the textwatcher to EditTexts like this

editText1.addTextWatcher()
editText2.addTextWatcher()
veeyikpong
  • 690
  • 7
  • 19
0

I've done something like this to have only one TextWatcher class to control as many EditText either from Activity OR Fragment

You need to first create a MultiTextWatcher class as below

class MultiTextWatcher {

    private var callback: TextWatcherWithInstance? = null

    fun setCallback(callback: TextWatcherWithInstance): MultiTextWatcher {
        this.callback = callback
        return this
    }


    fun registerEditText(editText: EditText): MultiTextWatcher {
        editText.addTextChangedListener(object : TextWatcher {
            override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
                callback!!.beforeTextChanged(editText, s, start, count, after)
            }

            override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
                callback!!.onTextChanged(editText, s, start, before, count)
            }

            override fun afterTextChanged(editable: Editable) {
                callback!!.afterTextChanged(editText, editable)
            }
        })

        return this
    }

     interface TextWatcherWithInstance {
        fun beforeTextChanged(editText: EditText, s: CharSequence, start: Int, count: Int, after: Int)

        fun onTextChanged(editText: EditText, s: CharSequence, start: Int, before: Int, count: Int)

        fun afterTextChanged(editText: EditText, editable: Editable)
    }
}

Then in your Activity or Fragment, you need to register as many EditText as you want like below, and also I've used Data Binding to get references of the XML views and you can use your ways.

 private fun setTextWatchers() {
        MultiTextWatcher()
            .registerEditText(binding.etCompanyAddress)
            .registerEditText(binding.etCompanyIntro)
            .registerEditText(binding.etCompanyName)
            .registerEditText(binding.etCompanyPhone)
            .setCallback(object : MultiTextWatcher.TextWatcherWithInstance {
                override fun beforeTextChanged(editText: EditText, s: CharSequence, start: Int, count: Int, after: Int) {
                }

                override fun onTextChanged(editText: EditText, s: CharSequence, start: Int, before: Int, count: Int) {
                    when (editText) {
                        binding.etCompanyAddress -> {
                            //do your logic here
                        }
                        binding.etCompanyPhone -> {
                            //do your logic here and so on
                        }
                    }
                }

                override fun afterTextChanged(editText: EditText, editable: Editable) {

                }
            })
    }
Suraj Bahadur
  • 2,901
  • 3
  • 18
  • 42