¿Cómo usar Single TextWatcher para múltiples EditTexts?

Respuestas:

190

Acabo de encontrar este problema. Lo resolví creando una implementación de clase interna TextWatcherque toma una vista como argumento. Luego, en la implementación del método, simplemente encienda la vista para ver de dónde Editableviene

Declaración:

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

Uso:

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));
Sky Kelsey
fuente
1
¿Puedes decir qué es 'modelo' en el código anterior y dónde declararlo?
YuDroid
33
Esta respuesta no es Single TextWatcher for multiple EditTexts. Son 3 instancias de una clase TextWatcher. Entonces 3 TextWatchers separados están controlando 3 EditTexts.
Bobs
2
La solución sugerida no es un TextWatcher para algunos EditTexts. Verifique esta respuesta: stackoverflow.com/a/13787221/779408
Bobs
2
Funciona de maravilla, combinando esto con un fragmento que contiene elementos de formulario para no perder datos al cambiar la orientación de aspecto.
Mathijs Segers
1
@breceivemail Para ser completamente justos, "TextWatcher único" no necesariamente significa una sola instancia, también podría ser una sola clase.
Malcolm
42

Si desea usar solo afterTextChanged, compare editables:

@Override
public void afterTextChanged(Editable editable) {
    if (editable == mEditText1.getEditableText()) {
        // DO STH
    } else if (editable == mEditText2.getEditableText()) {
        // DO STH
    }
}
Tomász
fuente
10
Esto solo funcionará correctamente si lo usa en ==lugar de .equals().
Jarett Millard
2
Eres genio! ¡Esto es solo comparar punteros, no valores reales almacenados en Editable! ¡Si!
Burak Tamtürk
3
¿Qué pasa si mEditText1 y mEditText2 tienen el mismo texto?
Tuss
@tuss por eso se comparan por referencia en lugar de valor
Joakim
1
@Tomasz ¿Cómo funciona esto? ¿Implementaste TextWatcher? ¿No tienes que anular tres métodos si lo haces?
leonheess
10

Implementación de 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);
    }
}

Uso

    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
fuente
¿Por qué crearías otra clase para encadenarlos? Quiero decir que todavía no sabes de dónde TextViewviene el cambio.
Farid
10

Si desea utilizar onTextChanged compare hashCode()mencionado a continuación -

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

}

O

Si desea usar afterTextChanged compare Editablemencionado a continuación -

@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
fuente
9

Funcionará con este código.

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

        }
    };

Luego agregue esto en oncreate

  mInputText.addTextChangedListener(watcher);
        e2.addTextChangedListener(watcher);
        e3.addTextChangedListener(watcher);
        e4.addTextChangedListener(watcher);
Asha Babu
fuente
Entonces, ¿de dónde es el mOutputText?
Kimi Chiu
5

Sé que este es un problema antiguo, y existe la decisión correcta. Escribiré el suyo, tal vez ayude a alguien.

Emulando el ejemplo clásico donde tenemos N EditText, y queremos mostrar el botón si todos los campos están llenos. Este ejemplo tiene sentido, especialmente si se utilizan validadores adicionales para cada uno.

Hice un ejemplo con respecto al problema, pero puedes hacer cualquier conjunto

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

    }
}

Se ve muy simple, ahora

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

Agregaré un diseño para que no pierdas el tiempo.

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
fuente
5

Haga que su clase herede de Activity e implemente TextWatcher.

Luego, a través de la magia del polimorfismo, solo necesita suscribirse a los eventos.

Esto no le dirá qué TextEdit cambió, sin embargo, utilizando una combinación de esto y la respuesta de Sky Kelsey , podría resolverlo muy bien.

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

Luego:

editText1.addTextChangedListener(watcher);
editText2.addTextChangedListener(watcher);
editText3.addTextChangedListener(watcher);
Cristian
fuente
2
(Advertencia de quejumbrosa) Probablemente tiene un código de validación muy similar para todos los controles y no quiere copiarlo y pegarlo 3 veces :) Lo he golpeado antes, ¿por qué pueden enviar el control que generó el clic en onClickListener y no en cosas como TextWatcher ... La única solución que se me ocurre es hacer 3 TextWatchers que llamen al mismo procedimiento pero con un puntero a sus respectivos controles de edición.
Torp
1
@Torp, @bie: Esta respuesta puede ser de interés: stackoverflow.com/questions/4283062/… No estoy seguro de que resuelva exactamente el problema aquí mencionado, pero, como se muestra, puede hacer que CustomTextWatcher llame automáticamente a otra función que tome en cuenta pasó Editable.
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
fuente
3

Esta es mi solución para kotlin. Simplemente puede usar la igualdad referencial (===) para verificar el mismo objeto y está funcionando perfectamente.

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
fuente
0

Sé que esta pregunta es antigua, sin embargo, quería compartir una de mis soluciones (en Kotlin). Mi solución es una mejora de la respuesta de @Shwarz Andrei, mi razón era qué pasaría si quisieras manipular más cosas / objetos.

En lugar de pasar ambos list of EditTextsy a Buttoncomo params, solo pasarías tu list of editText. Luego, dentro de su clase personalizada, implementaría un lambda como:

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

Luego lo colocará o elevará dentro del 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 
           }
       }
   }

Entonces, cada vez, hay un cambio en algunos EditText que se invoca su 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 
            }
        }
Lamour
fuente
0

Así es como lo hice:

Cree una ArrayList de EditTexts, y luego use un bucle for para aplicar TextWatcher para todos los EditTexts, si tiene un comportamiento para todos los editTexts, simplemente aplíquelo allí, si tiene comportamientos específicos para algunos editTexts específicos, entonces puede usar un if declaración para seleccionar y aplicar a editTexts individuales.

Aquí está mi código:

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

}

Espero que esto ayude

SamiHajjaj
fuente
Gracias por la respuesta, pero ¿es esta realmente la única forma de hacerlo? Quiero decir que parece un poco complicado para algo tan común como onTextChangeden un EditText. Agregar un onFocusChangepara múltiples widgets es mucho más simple, porque pasó el objeto remitente con la llamada al método. Luego puede examinar qué objeto ha activado la llamada y manejarlo desde allí.
BdR