Jak używać pojedynczego TextWatchera do wielu EditTextów?


Odpowiedzi:


190

Właśnie napotkałem ten problem. Rozwiązałem to, tworząc implementację klasy wewnętrznej, TextWatcherktóra przyjmuje View jako argument. Następnie w implementacji metody wystarczy włączyć widok, aby zobaczyć, z którego Editablepochodzi

Deklaracja:

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;
        }
    }
}

Stosowanie:

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));

1
Czy możesz powiedzieć, co to jest „model” w powyższym kodzie i gdzie go zadeklarować?
YuDroid,

33
ta odpowiedź nie jest Single TextWatcher for multiple EditTexts. Są to 3 wystąpienia jednej klasy TextWatcher. Tak więc 3 oddzielne TextWatchery kontrolują 3 EditTexty.
Bobs

2
Sugerowane rozwiązanie to nie jeden TextWatcher dla niektórych EditTexts. Sprawdź tę odpowiedź: stackoverflow.com/a/13787221/779408
Bobs

2
Działa jak urok, łącząc to z fragmentem zawierającym elementy formularza, aby nie utracić danych podczas zmiany orientacji aspektu.
Mathijs Segers

1
@breceivemail Aby być całkowicie uczciwym, „pojedynczy TextWatcher” niekoniecznie oznacza pojedynczą instancję, może to być również pojedyncza klasa.
Malcolm

42

Jeśli chcesz używać tylko afterTextChanged porównaj elementy do edycji:

@Override
public void afterTextChanged(Editable editable) {
    if (editable == mEditText1.getEditableText()) {
        // DO STH
    } else if (editable == mEditText2.getEditableText()) {
        // DO STH
    }
}

10
Będzie to działać poprawnie tylko wtedy, gdy użyjesz ==zamiast .equals().
Jarett Millard

2
Jesteś geniuszem! To tylko porównanie wskaźników, a nie rzeczywistych wartości przechowywanych w Editable! Tak!
Burak Tamtürk

3
co jeśli mEditText1 i mEditText2 mają ten sam tekst?
Tuss

@tuss, dlatego są porównywane przez odniesienie zamiast wartości
Joakim,

1
@Tomasz Jak to działa? Czy zaimplementowałeś TextWatcher? Czy nie musisz zastępować trzech metod, jeśli to zrobisz?
leonheess

10

Implementacja MultiTextWatcher

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);
    }
}

Stosowanie

    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
                }
            });

Dlaczego miałbyś tworzyć kolejną klasę, żeby je po prostu połączyć ?! Chodzi mi o to, że nadal nie wiesz, skąd TextViewpochodzi zmiana.
Farid

10

Jeśli chcesz użyć porównania onTextChangedhashCode() wspomnianego poniżej -

@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 
    }

}

Lub

Jeśli chcesz użyć porównania afterTextChangedEditable wspomnianego poniżej -

@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 
    }
}

9

Będzie działać z tym kodem

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);

        }
    };

Następnie dodaj to w oncreate

  mInputText.addTextChangedListener(watcher);
        e2.addTextChangedListener(watcher);
        e3.addTextChangedListener(watcher);
        e4.addTextChangedListener(watcher);

Skąd więc jest mOutputText?
Kimi Chiu,

5

Wiem, że to stary problem i jest to dobra decyzja. Napiszę własne, może komuś to pomoże.

Emulowanie klasycznego przykładu, w którym mamy N EditText i chcemy pokazać przycisk, jeśli wszystkie pola są wypełnione. Ten przykład ma sens, zwłaszcza jeśli dalej używamy walidatorów dla każdego z nich.

Zrobiłem przykład w odniesieniu do problemu, ale możesz zrobić dowolny zestaw

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);

    }
}

Teraz wygląda to bardzo prosto

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);
    }
  }
}

Dodam układ, żebyś nie tracił czasu

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>

5

Niech Twoja klasa odziedziczy po Activity i zaimplementuj TextWatcher.

Następnie, dzięki magii polimorfizmu, wystarczy zapisać się na wydarzenia.

To nie powie ci, co zmieniło TextEdit, jednak używając kombinacji tego i odpowiedzi Sky Kelsey , możesz to ładnie załatwić.

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
    }
}

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) {
    }
};

Następnie:

editText1.addTextChangedListener(watcher);
editText2.addTextChangedListener(watcher);
editText3.addTextChangedListener(watcher);

2
(Ostrzeżenie z jęczącym postem) Prawdopodobnie ma bardzo podobny kod walidacyjny dla wszystkich kontrolek i nie chce go kopiować i wklejać 3 razy :) Udało mi się to wcześniej, dlaczego mogą wysłać kontrolkę, która wygenerowała kliknięcie onClickListener, a nie na temat rzeczy takich jak TextWatcher ... Jedyne obejście, jakie przychodzi mi do głowy, to utworzenie 3 TextWatcherów, które wywołują tę samą procedurę, ale ze wskaźnikiem do odpowiednich kontrolek edycji.
Torp

1
@Torp, @bie: Ta odpowiedź może być interesująca: stackoverflow.com/questions/4283062/ ... Nie jestem pewien, czy to dokładnie rozwiązuje opisany tutaj problem, ale, jak pokazano, możesz sprawić, że CustomTextWatcher automatycznie wywoła inną funkcję, która przyjmuje przeszedł do edycji.
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;
                 }
             }   
         }
     }
}

3

To jest moje rozwiązanie dla kotlina. Możesz po prostu użyć równości referencyjnej (===), aby sprawdzić ten sam obiekt i działa idealnie.

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)

0

Wiem, że to pytanie jest stare, ale chciałem podzielić się jednym z moich rozwiązań (w Kotlinie). Moje rozwiązanie to ulepszenie odpowiedzi @Shwarz Andrei, moim powodem było to, że chciałeś manipulować większą ilością rzeczy / obiektów.

Zamiast przekazywać oba parametry list of EditTextsi a Buttonjako parametry, możesz przekazać tylko swoje list of editText. Następnie w swojej niestandardowej klasie zaimplementowałbyś lambdę taką:

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

Następnie ustawiasz lub podnosisz go wewnątrz 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 
           }
       }
   }

Tak więc za każdym razem następuje zmiana w jakimś EditText, w którym wywoływana jest twoja lambda

        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 
            }
        }

0

Oto jak to zrobiłem:

Utwórz ArrayList of EditTexts, a następnie użyj pętli for, aby zastosować TextWatcher dla wszystkich EditTexts, jeśli masz jedno zachowanie dla wszystkich editTexts, po prostu zastosuj je tam, jeśli masz określone zachowania dla niektórych konkretnych editTexts, możesz użyć if oświadczenie, aby wybrać i zastosować do poszczególnych editTexts.

Oto mój kod:

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                           
            }
        }
    });

}

Mam nadzieję że to pomoże


Dziękuję za odpowiedź, ale czy to naprawdę jedyny sposób, aby to zrobić? Chodzi mi o to, że wydaje się to trochę skomplikowane w przypadku czegoś tak powszechnego jak onTextChangedw EditText. Dodanie onFocusChangedla wielu widżetów jest znacznie prostsze, ponieważ przekazał obiekt nadawcy z wywołaniem metody. Następnie możesz sprawdzić, który obiekt wyzwolił wywołanie i stamtąd je obsłużyć.
BdR
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.