Як використовувати Single TextWatcher для декількох EditTexts?


Відповіді:


190

Я щойно стикався з цією проблемою. Я вирішив це, створивши внутрішню реалізацію класу, TextWatcherяка сприймає погляд як аргумент. Потім, у впровадженні методу, просто увімкніть подання, щоб побачити, з якого походження Editableйдеться

Декларація:

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

Використання:

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
Скажіть, будь ласка, що таке "модель" у наведеному вище коді та де це декларувати?
YuDroid

33
ця відповідь - ні Single TextWatcher for multiple EditTexts. Це 3 екземпляри одного класу TextWatcher. Отже, 3 окремі TextWatchers контролюють 3 EditTexts.
Бобс

2
Пропоноване рішення не є одним TextWatcher для деяких EditTexts. Перевірте цю відповідь: stackoverflow.com/a/13787221/779408
Бобс

2
Працює як шарм, поєднуючи це з фрагментом, який містить елементи форми, щоб не втрачати дані при зміні орієнтації на аспект.
Mathijs Segers

1
@breceivemail Щоб бути абсолютно справедливим, "єдиний TextWatcher" не обов'язково означає один екземпляр, він може бути і одним класом.
Малькольм

42

Якщо ви хочете використовувати лише післяTextChanged порівняти редагування:

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

10
Це буде працювати правильно лише якщо ви використовуєте ==замість .equals().
Джаретт Міллард

2
Ти геній! Це лише порівняння покажчиків, а не фактичних значень, що зберігаються на Editable! Так!
Бурак Тамтурк

3
що робити, якщо mEditText1 та mEditText2 мають однаковий текст?
Tuss

@tuss, тому їх порівнюють за посиланням замість значення
Йоакім

1
@Tomasz Як це працює? Ви реалізували TextWatcher? Чи не потрібно вам переосмислювати три методи, якщо вам це потрібно?
leonheess

10

Реалізація 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);
    }
}

Використання

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

Чому б ви створили інший клас, щоб просто їх ланцюжок ?! Я маю на увазі, що ти досі не знаєш, від якої TextViewзміни відбувається.
Фарид

10

Якщо ви хочете використовувати порівняння onTextChanged,hashCode() зазначене нижче -

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

}

Або

Якщо ви хочете використовувати порівняння afterTextChanged,Editable згадане нижче -

@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

Він буде працювати з цим кодом

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

        }
    };

Потім додайте це в oncreate

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

То звідки mOutputText?
Кімі Чіу

5

Я знаю, що це давнє питання, і є правильне рішення. Я напишу своє, можливо, це комусь допоможе.

Емуляція класичного прикладу, де у нас є N EditText, і ми хочемо показати кнопку, якщо всі поля заповнені. Цей приклад має сенс, особливо якщо надалі використовувати валідатори для кожного з них.

Я зробив приклад стосовно цього питання, але ви можете зробити будь-який набір

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

    }
}

Зараз це виглядає дуже просто

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

Я додам макет, щоб ви не витрачали часу

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

Спадковуйте свій клас від "Діяльність" та впроваджуйте TextWatcher.

Тоді через магію поліморфізму вам просто потрібно підписатися на події.

Це не скаже вам, що змінив TextEdit, однак, використовуючи комбінацію цього та відповіді Скай Келсі , ви можете це добре розібрати.

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

Тоді:

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

2
(Скидання після попередження) Він, ймовірно, має дуже схожий код перевірки для всіх елементів керування і не хоче копіювати та вставляти його 3 рази :) Я натискав його раніше, чому вони можуть надсилати елемент керування, який генерував натискання на onClickListener, а не на такі речі, як TextWatcher ... Єдине вирішення, про яке я можу придумати, - це зробити 3 TextWatchers, які викликають ту саму процедуру, але з вказівником на відповідні елементи редагування.
Торп

1
@Torp, @bie: Ця відповідь може зацікавити: stackoverflow.com/questions/4283062/… Не впевнений, що саме вона вирішує проблему, вказану тут, але, як показано, ви могли б мати змогу CustomTextWatcher автоматично викликати іншу функцію пройшли редагування.
Кевін Коппок

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

Це моє рішення для котлін. Ви можете просто скористатися довідковою рівністю (===), щоб перевірити той самий об'єкт, і він працює ідеально.

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

Я знаю, що це питання давнє, але я хотів поділитися одним зі своїх рішень (у Котліні). Моє рішення - вдосконалення відповіді @Shwarz Andrei, моєю причиною було те, що якщо ви хочете маніпулювати більше речами / предметами.

Замість передачі обох list of EditTextsі a Buttonяк парамів, ви здасте лише свій list of editText. Потім у вашому користувальницькому класі ви реалізуєте лямбда:

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

Тоді ви встановите або піднімете його всередині 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 
           }
       }
   }

Тому кожен раз, коли відбувається зміна деяких EditText ваших лямбда-викликів

        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

Ось як я це зробив:

Створіть ArrayList з EditTexts, а потім за допомогою циклу для циклу застосуйте TextWatcher для всіх EditTexts, якщо у вас є одна поведінка для всіх editTexts, то просто застосуйте його там, якщо ви визначаєте поведінку для певних editTexts, тоді ви можете використовувати a заявку, щоб вибрати та застосувати до окремих editTexts.

Ось мій код:

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

}

Сподіваюся, це допомагає


Дякую за відповідь, але чи справді це єдиний спосіб зробити це? Я маю на увазі, що це здається складною для чогось такого поширеного, як у onTextChangedEditText. Додавання onFocusChangeдекількох віджетів набагато простіше, оскільки він передав об'єкт відправника за допомогою виклику методу. Потім ви можете вивчити, який об’єкт викликав виклик, і обробити його звідти.
BdR
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.