Wie verwende ich Single TextWatcher für mehrere EditTexts?

Antworten:

190

Ich bin gerade auf dieses Problem gestoßen. Ich habe es gelöst, indem ich eine Implementierung der inneren Klasse erstellt habe TextWatcher, die eine Ansicht als Argument verwendet. Schalten Sie dann in der Methodenimplementierung einfach die Ansicht ein, um zu sehen, von welcher die Editablestammt

Erklärung:

private class GenericTextWatcher implements TextWatcher{

    private View view;
    private GenericTextWatcher(View view) {
        this.view = view;
    }

    public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
    public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}

    public void afterTextChanged(Editable editable) {
        String text = editable.toString();
        switch(view.getId()){
            case R.id.name:
                model.setName(text);
                break;
            case R.id.email:
                model.setEmail(text);
                break;
            case R.id.phone:
                model.setPhone(text);
                break;
        }
    }
}

Verwendung:

name = (EditText) findViewById(R.id.name);
name.setText(model.getName());
name.addTextChangedListener(new GenericTextWatcher(name));

email = (EditText) findViewById(R.id.email);
email.setText(model.getEmail());
email.addTextChangedListener(new GenericTextWatcher(email));

phone = (EditText) findViewById(R.id.phone);
phone.setText(model.getPhone());
phone.addTextChangedListener(new GenericTextWatcher(phone));
Himmel Kelsey
quelle
1
Können Sie bitte sagen, was im obigen Code "Modell" ist und wo es deklariert werden soll?
YuDroid
33
Diese Antwort ist nicht Single TextWatcher for multiple EditTexts. Es sind 3 Instanzen einer TextWatcher-Klasse. 3 separate TextWatcher steuern also 3 EditTexts.
Bobs
2
Die vorgeschlagene Lösung ist für einige EditTexts kein einziger TextWatcher. Überprüfen Sie diese Antwort: stackoverflow.com/a/13787221/779408
Bobs
2
Funktioniert wie ein Zauber und kombiniert dies mit einem Fragment, das Formularelemente enthält, um beim Ändern der Aspektorientierung keine Daten zu verlieren.
Mathijs Segers
1
@breceivemail Um ganz fair zu sein, bedeutet "einzelner TextWatcher" nicht unbedingt eine einzelne Instanz, sondern könnte auch eine einzelne Klasse sein.
Malcolm
42

Wenn Sie nur afterTextChanged verwenden möchten, vergleichen Sie die bearbeitbaren Dateien:

@Override
public void afterTextChanged(Editable editable) {
    if (editable == mEditText1.getEditableText()) {
        // DO STH
    } else if (editable == mEditText2.getEditableText()) {
        // DO STH
    }
}
Tomasz
quelle
10
Dies funktioniert nur dann korrekt, wenn Sie ==anstelle von verwenden .equals().
Jarett Millard
2
Du bist ein Genie! Dies ist nur ein Vergleich von Zeigern, nicht von tatsächlichen Werten, die auf Editable gespeichert sind! Ja!
Burak Tamtürk
3
Was ist, wenn mEditText1 und mEditText2 denselben Text haben?
Tuss
@tuss deshalb werden sie durch Referenz statt Wert verglichen
Joakim
1
@Tomasz Wie funktioniert das? Haben Sie TextWatcher implementiert? Müssen Sie dabei nicht drei Methoden überschreiben?
Leonheess
10

MultiTextWatcher-Implementierung

public class MultiTextWatcher {

    private TextWatcherWithInstance callback;

    public MultiTextWatcher setCallback(TextWatcherWithInstance callback) {
        this.callback = callback;
        return this;
    }

    public MultiTextWatcher registerEditText(final EditText editText) {
        editText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                callback.beforeTextChanged(editText, s, start, count, after);
            }

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

            @Override
            public void afterTextChanged(Editable editable) {
                callback.afterTextChanged(editText, editable);
            }
        });

        return this;
    }

    interface TextWatcherWithInstance {
        void beforeTextChanged(EditText editText, CharSequence s, int start, int count, int after);

        void onTextChanged(EditText editText, CharSequence s, int start, int before, int count);

        void afterTextChanged(EditText editText, Editable editable);
    }
}

Verwendung

    new MultiTextWatcher()
            .registerEditText(editText1)
            .registerEditText(editText2)
            .registerEditText(editText3)
            .setCallback(new TextWatcherWithInstance() {
                @Override
                public void beforeTextChanged(EditText editText, CharSequence s, int start, int count, int after) {
                    // TODO: Do some thing with editText
                }

                @Override
                public void onTextChanged(EditText editText, CharSequence s, int start, int before, int count) {
                    // TODO: Do some thing with editText
                }

                @Override
                public void afterTextChanged(EditText editText, Editable editable) {
                    // TODO: Do some thing with editText
                }
            });
Ilya Gazman
quelle
Warum würden Sie eine andere Klasse erstellen, um sie einfach zu verketten?! Ich meine, Sie wissen immer noch nicht, woher TextViewdie Veränderung kommt.
Farid
10

Wenn Sie onTextChanged verwenden möchten, vergleichen Sie hashCode()die unten genannten -

@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
    if(charSequence.hashCode() == first_edit_text.getText().hashCode()){
        // do other things 
    }

    if(charSequence.hashCode() == second_edit_text.getText().hashCode()){
       // do other things 
    }

}

Oder

Wenn Sie afterTextChanged verwenden möchten, vergleichen Sie Editabledie unten genannten -

@Override
public void afterTextChanged(Editable editable) {
    if (editable == first_edit_text.getEditableText()) {
        // do other things 
    } else if (editable == second_edit_text.getEditableText()) {
       // do other things 
    }
}
Aman Jham
quelle
9

Es wird mit diesem Code funktionieren

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

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

        @Override
        public void afterTextChanged(Editable s) {
          String outputedText = s.toString();

  mOutputText.setText(outputedText);

        }
    };

Fügen Sie dies dann in oncreate hinzu

  mInputText.addTextChangedListener(watcher);
        e2.addTextChangedListener(watcher);
        e3.addTextChangedListener(watcher);
        e4.addTextChangedListener(watcher);
Asha Babu
quelle
Woher kommt also der mOutputText?
Kimi Chiu
5

Ich weiß, dass dies ein altes Problem ist und es die richtige Entscheidung gibt. Ich werde ihre eigenen schreiben, vielleicht hilft es jemandem.

Emulieren Sie das klassische Beispiel, in dem wir N EditText haben, und wir möchten die Schaltfläche anzeigen, wenn alle Felder ausgefüllt sind. Dieses Beispiel ist sinnvoll, insbesondere wenn für jeden weitere Validatoren verwendet werden.

Ich habe ein Beispiel in Bezug auf das Problem gemacht, aber Sie können jeden Satz machen

MultiEditText.class

public class MultiEditText extends AppCompatActivity{

EditText ed_1, ed_2, ed_3;
Button btn_ok;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.multi_edit_text);

    ed_1 = (EditText) findViewById(R.id.ed_1);
    ed_2 = (EditText) findViewById(R.id.ed_2);
    ed_3 = (EditText) findViewById(R.id.ed_3);
    btn_ok = (Button) findViewById(R.id.btn_ok);
    btn_ok.setEnabled(false);

    //if want more here can cycle interface List

     EditText[] edList = {ed_1, ed_2, ed_3};
     CustomTextWatcher textWatcher = new CustomTextWatcher(edList, btn_ok);
     for (EditText editText : edList) editText.addTextChangedListener(textWatcher);

    }
}

Es sieht jetzt sehr einfach aus

CustomTextWatcher.class

public class CustomTextWatcher implements TextWatcher {

View v;
EditText[] edList;

public CustomTextWatcher(EditText[] edList, Button v) {
    this.v = v;
    this.edList = edList;
}

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

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

@Override
public void afterTextChanged(Editable s) {
    for (EditText editText : edList) {
        if (editText.getText().toString().trim().length() <= 0) {
            v.setEnabled(false);
            break;
        }
        else v.setEnabled(true);
    }
  }
}

Ich werde ein Layout hinzufügen, damit Sie keine Zeit verschwenden

multi_edit_text.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">

<EditText
    android:id="@+id/ed_1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentTop="true"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="8dp" />

<EditText
    android:id="@+id/ed_2"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_below="@+id/ed_1"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="8dp" />

<EditText
    android:id="@+id/ed_3"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_below="@+id/ed_2"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="8dp" />

<Button
    android:id="@+id/btn_ok"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_below="@+id/ed_3"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="8dp"
    android:text="OK" />
</RelativeLayout>
Shwarz Andrei
quelle
5

Lassen Sie Ihre Klasse von Activity erben und implementieren Sie TextWatcher.

Dann müssen Sie durch die Magie des Polymorphismus nur noch die Ereignisse abonnieren.

Dies sagt Ihnen nicht, was TextEdit geändert hat, aber wenn Sie eine Kombination aus dieser und der Antwort von Sky Kelsey verwenden , können Sie das gut klären.

public YourActivity extends Activity implements TextWatcher {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_YourActivity);

        //Subscribe to the events
        EditText txt1 = (EditText) findViewById(R.id.txt1);
        txt1.addTextChangedListener(this);

        EditText txt2 = (EditText) findViewById(R.id.txt2);
        txt2.addTextChangedListener(this);
    }

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

            EditText txt1 = (EditText) findViewById(R.id.txt1);
            EditText txt2 = (EditText) findViewById(R.id.txt2);
            // You probably only want the text value from the EditText. But you get the idea. 
                doStuff(txt1,txt2);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.calc, menu);
        return true;
    }

    @Override
    public void afterTextChanged(Editable s) {
        // TODO Auto-generated method stub
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        // TODO Auto-generated method stub
    }
}
NitroxDM
quelle
3
TextWatcher watcher = 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) {
    }

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

Dann:

editText1.addTextChangedListener(watcher);
editText2.addTextChangedListener(watcher);
editText3.addTextChangedListener(watcher);
Cristian
quelle
2
(Jammernde Post-Warnung) Er hat wahrscheinlich einen sehr ähnlichen Validierungscode für alle Steuerelemente und möchte ihn nicht dreimal kopieren und einfügen :) Ich habe ihn schon einmal getroffen, warum können sie das Steuerelement senden, das den Klick auf onClickListener generiert hat und nicht zu Dingen wie TextWatcher ... Die einzige Problemumgehung, die ich mir vorstellen kann, besteht darin, 3 TextWatcher zu erstellen, die dieselbe Prozedur aufrufen, jedoch mit einem Zeiger auf ihre jeweiligen Bearbeitungssteuerelemente.
Torp
1
@Torp, @bie: Diese Antwort könnte von Interesse sein: stackoverflow.com/questions/4283062/… Nicht sicher, ob sie das hier angegebene Problem genau löst, aber wie gezeigt, könnte der CustomTextWatcher automatisch eine andere Funktion aufrufen, die die Funktion übernimmt Editable bestanden.
Kevin Coppock
3
public class MainActivity extends AppCompatActivity{
    EditText value1, value2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //instantiate EditText controls
        value1 = (EditText)findViewById(R.id.txtValue1);
        value2 = (EditText)findViewById(R.id.txtValue2);

        //set up text changed listener
        value1.addTextChangedListener(new TextChange(value1));               
        value2.addTextChangedListener(new TextChange(value2));                       

        //inner class
        private class TextChange implements TextWatcher {

             View view;
             private TextChange (View v) {
                 view = v;
             }

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

             }


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

                 switch (view.getId()) {
                     case R.id.txtValue1:
                         //insert your TextChangedListener codes here
                         break;

                     case R.id.txtValue2:
                         //insert your TextChangedListener codes here
                         break;
                 }
             }   
         }
     }
}
Ng Kian Keong
quelle
3

Dies ist meine Lösung für Kotlin. Sie können einfach die referenzielle Gleichheit (===) verwenden, um dasselbe Objekt zu überprüfen, und es funktioniert einwandfrei.

val mTextWatcher = object : TextWatcher {
        override fun afterTextChanged(et: Editable?) {

            when {
                et === et1.editableText -> {
                    Toast.makeText(this@MainActivity, "EditText 1", Toast.LENGTH_LONG).show()
                }
                et === et2.editableText -> {
                    Toast.makeText(this@MainActivity, "EditText 2", Toast.LENGTH_LONG).show()
                }

            }
        }

        override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
        }
        override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
        }
    }
    et1.addTextChangedListener(mTextWatcher)
    et2.addTextChangedListener(mTextWatcher)
Shohan Ahmed Sijan
quelle
0

Ich weiß, dass diese Frage alt ist, aber ich wollte eine meiner Lösungen (in Kotlin) teilen. Meine Lösung ist eine Verbesserung der Antwort von @Shwarz Andrei. Mein Grund war, wenn Sie mehr Dinge / Objekte manipulieren wollten.

Anstatt beide list of EditTextsund a Buttonals Parameter zu übergeben, würden Sie nur Ihre übergeben list of editText. Dann würden Sie in Ihrer benutzerdefinierten Klasse ein Lambda implementieren, z.

var hasFilled:((Boolean)->Unit)? = null 

Dann setzen oder heben Sie es in der afterTextChanged

override fun afterTextChanged(p0: Editable?) {
       for (edit in _editTextList) {
           if (edit?.text.toString().trim().isEmpty()) {
                 hasFilled?.invoke(false) //<-- here 
               break
           } else {
               hasFilled?.invoke(true) //<--- here 
           }
       }
   }

Jedes Mal, wenn sich in einem EditText etwas ändert, wird Ihr Lambda aufgerufen

        val editTexts = listOf(emailEditText,passwordEditText) // your list of editText
        val textWatcher = customTextWatcher(editTexts) // initialize your custom object 
        editTexts.forEach { it -> it?.addTextChangedListener(textWatcher) } // each editText would listen for changes 


        textWatcher.hasFilled = { value ->  // now you have access to your lambda 
            if (value != true)  {
               // change the state of the button to unable 
              // do other things 
            } else {
              // change the state of the button to enable 
              // do other things 
            }
        }
Lamour
quelle
0

So habe ich es gemacht:

Erstellen Sie eine ArrayList von EditTexts und verwenden Sie dann eine for-Schleife, um den TextWatcher für alle EditTexts anzuwenden. Wenn Sie ein Verhalten für alle editTexts haben, wenden Sie es einfach dort an. Wenn Sie bestimmte Verhaltensweisen für bestimmte editTexts festlegen, können Sie ein if verwenden Anweisung zum Auswählen und Anwenden auf einzelne editTexts.

Hier ist mein Code:

ArrayList<EditText> editTexts = new ArrayList<>(); // Container list

editText1 = (EditText) findViewById(R.id.editText1);
editText2 = (EditText) findViewById(R.id.editText2);
editText3 = (EditText) findViewById(R.id.editText3);

editTexts.add(editText1); // editTexts[0]
editTexts.add(editText2); // editTexts[1]
editTexts.add(editText3); // editTexts[2]

for (final EditText editText : editTexts) { //need to be final for custom behaviors 
    editText.addTextChangedListener(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) {

        }

        @Override
        public void afterTextChanged(Editable s) {
            //Apply general behavior for all editTexts

            if (editText == editTexts.get(1)) {
                //Apply custom behavior just for this editText                           
            }
        }
    });

}

Hoffe das hilft

SamiHajjaj
quelle
Danke für die Antwort, aber ist das wirklich der einzige Weg, dies zu tun? Ich meine, es scheint onTextChangedziemlich kompliziert für etwas, das so häufig ist wie auf einem EditText. Das Hinzufügen eines onFocusChangefür mehrere Widgets ist viel einfacher, da das Absenderobjekt mit dem Methodenaufruf übergeben wurde. Anschließend können Sie untersuchen, welches Objekt den Aufruf ausgelöst hat, und ihn von dort aus bearbeiten.
BdR