Android EditText - evento terminado de escribir

122

Quiero capturar un evento cuando el usuario termine de editar EditText.

¿Cómo puede hacerse esto?

eddyuk
fuente
1
@PadmaKumar La forma más natural sería si el enfoque se pierde en el EditText; entonces el usuario definitivamente ha terminado, sin embargo, por experiencia, parece que no todos los widgets de Android están captando el enfoque correctamente (tuve esos problemas con Spinners y Buttons) - para que otros widgets no pierdan el foco ...
AgentKnopf
3
¿Por qué una plataforma tan grande nos permite lidiar con este tipo de funciones simples? Google no perderá nada si agregan tales características principales a sus bibliotecas de soporte al menos
MBH
Si sabe la longitud exacta de la cadena que el usuario ingresará, obtenga el texto afterTextChanged. Ej .:override fun afterTextChanged(s: Editable?) { if (s.toString().length == 5) { val enteredString = s.toString() }
Shylendra Madda
2
Me estoy rascando la cabeza preguntándome por qué esto es tan difícil en Android que nosotros (yo incluido) necesitamos buscar e iniciar una discusión en línea y muchas soluciones diferentes con más de 10 líneas de código ...?
nsandersen

Respuestas:

119

Cuando el usuario haya terminado de editar, presionará DoneoEnter

((EditText)findViewById(R.id.youredittext)).setOnEditorActionListener(
    new EditText.OnEditorActionListener() {
        @Override
        public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
            if (actionId == EditorInfo.IME_ACTION_SEARCH ||
                    actionId == EditorInfo.IME_ACTION_DONE ||
                    event != null &&
                    event.getAction() == KeyEvent.ACTION_DOWN &&
                    event.getKeyCode() == KeyEvent.KEYCODE_ENTER) {
                if (event == null || !event.isShiftPressed()) {
                   // the user is done typing. 

                   return true; // consume.
                }                
            }
            return false; // pass on to other listeners. 
        }
    }
);
Reno
fuente
50
¿Puedes estar seguro de ello? Tengo el hábito de acceder al siguiente campo editable (casi nunca presiono Enter / Done) y lo que he visto de nuestros clientes, tampoco ellos ... Estoy hablando de una lista de Edittexts / Comboboxes, etc. .Si solo le da al usuario un campo de entrada y lo fuerza a presionar un botón antes de que pueda avanzar a otros campos, entonces obviamente es una historia diferente ...
AgentKnopf
5
Recuerde, también debe tener android: singleLine = "true" configurado para su texto de edición. Gracias a stackoverflow.com/questions/15901863/…
steven smith
1
Buena solución, pero puede fallar con un puntero nulo ... 'event' puede ser nulo en la acción Done, por lo que event.isShiftPressed () se bloquea. Si usa esto, agregue comprobaciones de puntero nulo.
Georgie
Se agregó la edición para verificar si hay un "Evento" nulo.
Word Rearranger
3
¿Qué pasa si presionas el botón ATRÁS que cierra el teclado?
user347187
183

Mejor manera, también puede usar EditText en el oyente FocusChange para verificar si el usuario ha realizado la edición: (No es necesario que el usuario presione el botón Listo o Enter en el teclado en pantalla)

 ((EditText)findViewById(R.id.youredittext)).setOnFocusChangeListener(new OnFocusChangeListener() {

    @Override
    public void onFocusChange(View v, boolean hasFocus) {

      // When focus is lost check that the text field has valid values.

      if (!hasFocus) { {
         // Validate youredittext
      }
    }
 });

Nota: Para más de un EditText, también puede permitir que su clase implemente, View.OnFocusChangeListenerluego configure los oyentes para cada uno de ustedes EditText y validelos como se muestra a continuación.

((EditText)findViewById(R.id.edittext1)).setOnFocusChangeListener(this);
((EditText)findViewById(R.id.edittext2)).setOnFocusChangeListener(this);

    @Override
    public void onFocusChange(View v, boolean hasFocus) {

      // When focus is lost check that the text field has valid values.

      if (!hasFocus) {
        switch (view.getId()) {
           case R.id.edittext1:
                 // Validate EditText1
                 break;
           case R.id.edittext2:
                 // Validate EditText2
                 break;
        }
      }
    }
Vinayak Bevinakatti
fuente
60
Esto no funciona si solo hay un enfocable EditText. Nunca perderá el enfoque.
Bart Friederichs
¿Cuál es la mejor forma si solo tenemos una EditText? ¿Tengo que usar onEditorAction?
Tux
3
Si solo hay uno EditText, valida lo que haga el usuario para salir de la pantalla.
Christine
Intenté esto pero no funcionó. Cada vez que presioné Intro me daría un mensaje similar a este: W/ViewRootImpl: Cancelling event due to no window focus: MotionEvent { action=ACTION_CANCEL, actionButton=0, id[0]=0, x[0]=605.52246, y[0]=969.4336, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=238238, downTime=235422, deviceId=0, source=0x1002 }Aunque escribí algo. Este es un texto de edición que solo acepta números.
ahitt6345
1
Hay un punto al lado de usar el oyente OnFocusChange: ¡no se llamará si EditText tiene el foco y el dispositivo se gira! Lo resolví poniendo someOtherView.requestFocus () en Activities onPause (). (antes de super.onPause ()) De esa manera, es seguro que EditText pierda el foco y se llame al oyente.
allofmex
60

Personalmente, prefiero el envío automático después de finalizar la escritura. Así es como puede detectar este evento.

Declaraciones e inicialización:

private Timer timer = new Timer();
private final long DELAY = 1000; // in ms

Oyente en, por ejemplo, onCreate ()

EditText editTextStop = (EditText) findViewById(R.id.editTextStopId);
    editTextStop.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count,
                int after) {
        }
        @Override
        public void onTextChanged(final CharSequence s, int start, int before,
                int count) {
            if(timer != null)
                timer.cancel();
        }
        @Override
        public void afterTextChanged(final Editable s) {
            //avoid triggering event when text is too short
            if (s.length() >= 3) {              

                timer = new Timer();
                timer.schedule(new TimerTask() {
                    @Override
                    public void run() {
                        // TODO: do what you need here (refresh list)
                        // you will probably need to use
                        // runOnUiThread(Runnable action) for some specific
                        // actions
                        serviceConnector.getStopPoints(s.toString());
                    }

                }, DELAY);
            }
        }
    });

Entonces, cuando se cambia el texto, el temporizador comienza a esperar a que ocurran los próximos cambios. Cuando ocurren, el temporizador se cancela y luego se inicia una vez más.

ZimaXXX
fuente
11
esto es increíble, pero tenga en cuenta que los temporizadores pueden causar pérdidas de memoria, así que úselo en su Handlerlugar
Moh Mah
Es genial, Thansk.
VipPunkJoshers Droopy
¿Qué es serviceConnector?
altruista
@altruistic Es solo un fragmento de mi código del que extraje la respuesta. Es el lugar donde debería ir la lógica.
ZimaXXX
21

Puedes hacerlo usando setOnKeyListener o usando un textWatcher como:

Establecer observador de texto editText.addTextChangedListener(textWatcher);

luego llame

private TextWatcher textWatcher = new TextWatcher() {

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

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

        @Override
        public void afterTextChanged(Editable s) {

        }
    };
Brillar abajo
fuente
27
Me temo que esta no es la solución (completa): el TextWatcher se activa después de cada edición; si escribe hola, se activará para h, e, l, lyo; en realidad, no sabe cuándo el usuario ha "terminado". . Sería genial si el TextWatcher realmente supiera cuándo el widget perdió el foco y el usuario siguió adelante ...
AgentKnopf
4

Aunque muchas respuestas apuntan en la dirección correcta, creo que ninguna de ellas responde a lo que estaba pensando el autor de la pregunta. O al menos entendí la pregunta de manera diferente porque estaba buscando una respuesta a un problema similar. El problema es "Cómo saber cuándo el usuario deja de escribir sin que él presione un botón" y desencadenar alguna acción (por ejemplo, autocompletar). Si desea hacer esto, inicie el temporizador en onTextChanged con un retraso que consideraría que el usuario dejó de escribir (por ejemplo, 500-700ms), para cada nueva letra cuando inicie el temporizador, cancele la anterior (o al menos use algún tipo de marcar que cuando marcan no hacen nada). Aquí hay un código similar al que he usado:

new Timer().schedule(new TimerTask() {
  @Override
  public void run() {
     if (!running) {                            
        new DoPost().execute(s.toString());
  });
 }
}, 700);

Tenga en cuenta que modifico el indicador booleano en ejecución dentro de mi tarea asíncrona (Task obtiene el json del servidor para autocompletar).

También tenga en cuenta que esto crea muchas tareas de temporizador (creo que están programadas en el mismo hilo, pero tendría que verificar esto), por lo que probablemente haya muchos lugares para mejorar, pero este enfoque también funciona y la conclusión es que debería usar un temporizador ya que no hay ningún evento "El usuario dejó de escribir"

Igor Čordaš
fuente
¿Puede dar más información y código de muestra sobre esto?
John Ernest Guadalupe
La esencia del código está en la respuesta, para obtener un ejemplo más detallado, consulte la respuesta de ZimaXXX en esta misma página que utiliza el mismo enfoque. No sé qué más información podría agregar, si puede especificar lo que no está claro, intentaré agregar detalles.
Igor Čordaš
4

tanto @Reno como @Vinayak B responden juntos si quieren ocultar el teclado después de la acción

textView.setOnEditorActionListener(new EditText.OnEditorActionListener() {
    @Override
    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
        if (actionId == EditorInfo.IME_ACTION_SEARCH || actionId == EditorInfo.IME_ACTION_DONE) {
            InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(textView.getWindowToken(), 0);
            return true;
        }
        return false;
    }
});

textView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
    @Override
    public void onFocusChange(View v, boolean hasFocus) {
        if (!hasFocus) {
             // your action here
        }
    }
});
Tyler Davis
fuente
3

Un enfoque diferente ... aquí hay un ejemplo: si el usuario tiene un retraso de 600-1000ms cuando está escribiendo, puede considerar que está detenido.

 myEditText.addTextChangedListener(new TextWatcher() {
             
            private String s;
            private long after;
			private Thread t;
            private Runnable runnable_EditTextWatcher = new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        if ((System.currentTimeMillis() - after) > 600)
                        {
                            Log.d("Debug_EditTEXT_watcher", "(System.currentTimeMillis()-after)>600 ->  " + (System.currentTimeMillis() - after) + " > " + s);
                            // Do your stuff
                            t = null;
                            break;
                        }
                    }
                }
            };
            
            @Override
            public void onTextChanged(CharSequence ss, int start, int before, int count) {
                s = ss.toString();
            }
            
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }
            
            @Override
            public void afterTextChanged(Editable ss) {
                after = System.currentTimeMillis();
                if (t == null)
                {
                    t = new Thread(runnable_EditTextWatcher);
                      t.start();
                }
            }
        });

Cristi Maris
fuente
2

Bien, esto funcionará al 100% seguro.

Primero deberá configurar el oyente si el teclado se muestra u oculta. Si se muestra el teclado, probablemente el usuario esté escribiendo; de lo contrario, termine de escribir.

final View activityRootView = findViewById(android.R.id.content);
        activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {

                    Rect r = new Rect();
                    //r will be populated with the coordinates of your view that area still visible.
                    activityRootView.getWindowVisibleDisplayFrame(r);

                    int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
                    if (heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...

                        isTyping = true;
                    } else {
//to make sure this will call only once when keyboard is hide.
                        if(isTyping){
                            isTyping = false;
                        }
                    }
            }
        });
Krit
fuente
1
¿Qué pasa con las ediciones de texto múltiples? Y las heightDiff > 100cosas dan miedo en mi humilde opinión.
Bondax
No hay problema, funciona bien. Eche un vistazo a este stackoverflow.com/questions/2150078/…
Krit
2

Resolví este problema de esta manera. Usé kotlin.

        var timer = Timer()
        var DELAY:Long = 2000

        editText.addTextChangedListener(object : TextWatcher {

            override fun afterTextChanged(s: Editable?) {
                Log.e("TAG","timer start")
                timer = Timer()
                timer.schedule(object : TimerTask() {
                    override fun run() {
                        //do something
                    }
                }, DELAY)
            }

            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}

            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
                Log.e("TAG","timer cancel ")
                timer.cancel() //Terminates this timer,discarding any currently scheduled tasks.
                timer.purge() //Removes all cancelled tasks from this timer's task queue.
            }
        })
Fahed Hermoza
fuente
0

Tuve el mismo problema y no quería depender de que el usuario presionara Listo o Enter.

Mi primer intento fue usar el oyente onFocusChange, pero ocurrió que mi EditText obtuvo el foco de forma predeterminada. Cuando el usuario presionaba alguna otra vista, onFocusChange se activaba sin que el usuario le hubiera asignado el foco.

La siguiente solución lo hizo por mí, donde onFocusChange se adjunta si el usuario toca EditText:

final myEditText = new EditText(myContext); //make final to refer in onTouch
myEditText.setOnTouchListener(new OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            myEditText.setOnFocusChangeListener(new OnFocusChangeListener() {

                @Override
                public void onFocusChange(View v, boolean hasFocus) {
                    if(!hasFocus){
                        // user is done editing
                    }
                }
            }
        }
}

En mi caso, cuando el usuario terminó de editar la pantalla se volvió a renderizar, renovando así el objeto myEditText. Si se mantiene el mismo objeto, probablemente debería eliminar el oyente onFocusChange en onFocusChange para evitar el problema onFocusChange descrito al comienzo de esta publicación.

Jos
fuente
Está configurando un oyente de cambio de enfoque cada vez que ocurre un evento táctil, que será varias veces por segundo. No hagas esto.
jsonfry
0

Tuve el mismo problema al intentar implementar "ahora escribiendo" en la aplicación de chat. intente extender EditText de la siguiente manera:

public class TypingEditText extends EditText implements TextWatcher {

private static final int TypingInterval = 2000;


public interface OnTypingChanged {
    public void onTyping(EditText view, boolean isTyping);
}
private OnTypingChanged t;
private Handler handler;
{
    handler = new Handler();
}
public TypingEditText(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    this.addTextChangedListener(this);
}

public TypingEditText(Context context, AttributeSet attrs) {
    super(context, attrs);
    this.addTextChangedListener(this);
}

public TypingEditText(Context context) {
    super(context);
    this.addTextChangedListener(this);
}

public void setOnTypingChanged(OnTypingChanged t) {
    this.t = t;
}

@Override
public void afterTextChanged(Editable s) {
    if(t != null){
        t.onTyping(this, true);
        handler.removeCallbacks(notifier);
        handler.postDelayed(notifier, TypingInterval);
    }

}

private Runnable notifier = new Runnable() {

    @Override
    public void run() {
        if(t != null)
            t.onTyping(TypingEditText.this, false);
    }
};

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


@Override
public void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) { }

}

Nadav
fuente
0

Terminé con ella con el mismo problema y no pude usar la solución con onEditorAction o onFocusChange y no quería probar el temporizador. Un temporizador es demasiado peligroso para su gusto, debido a todos los hilos y demasiado impredecible, ya que no sabe cuándo se ejecuta el código.

El onEditorAction no detecta cuando el usuario se va sin usar un botón y, si lo usa, tenga en cuenta que KeyEvent puede ser nulo. El enfoque no es confiable en ambos extremos, el usuario puede enfocarse y salir sin ingresar ningún texto o seleccionar el campo y el usuario no necesita salir del último campo EditText.

Mi solución usa onFocusChange y una bandera establecida cuando el usuario comienza a editar texto y una función para obtener el texto de la última vista enfocada, a la que llamo cuando lo necesito.

Simplemente borro el foco en todos mis campos de texto para engañar el código de vista de texto de salida. El código clearFocus solo se ejecuta si el campo tiene el foco. Llamo a la función en onSaveInstanceState para no tener que guardar la bandera (mEditing) como un estado de la vista EditText y cuando se hace clic en botones importantes y cuando se cierra la actividad.

Tenga cuidado con TexWatcher, ya que es una llamada que a menudo uso la condición en el foco para no reaccionar cuando el código onRestoreInstanceState ingresa texto. yo

final EditText mEditTextView = (EditText) getView();

    mEditTextView.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) {
            if (!mEditing && mEditTextView.hasFocus()) {
                mEditing = true;
            }
        }
    });
    mEditTextView.setOnFocusChangeListener(new View.OnFocusChangeListener() {

        @Override
        public void onFocusChange(View v, boolean hasFocus) {
            if (!hasFocus && mEditing) {
                mEditing = false;
                ///Do the thing
            }
        }
    });
protected void saveLastOpenField(){
    for (EditText view:getFields()){
            view.clearFocus();
    }
}
Rezder
fuente
0

He hecho algo como esta clase abstracta que se puede usar en lugar del tipo TextView.OnEditorActionListener.

abstract class OnTextEndEditingListener : TextView.OnEditorActionListener {

    override fun onEditorAction(textView: TextView?, actionId: Int, event: KeyEvent?): Boolean {

        if(actionId == EditorInfo.IME_ACTION_SEARCH ||
                actionId == EditorInfo.IME_ACTION_DONE ||
                actionId == EditorInfo.IME_ACTION_NEXT ||
                event != null &&
                event.action == KeyEvent.ACTION_DOWN &&
                event.keyCode == KeyEvent.KEYCODE_ENTER) {

            if(event == null || !event.isShiftPressed) {
                // the user is done typing.
                return onTextEndEditing(textView, actionId, event)
            }
        }
        return false // pass on to other listeners
    }

    abstract fun onTextEndEditing(textView: TextView?, actionId: Int, event: KeyEvent?) : Boolean
}
Michał Ziobro
fuente
0

Fácil de activar, termine de escribir en EditText

funcionó para mí, si usa java, conviértalo

En Kotlin

youredittext.doAfterTextChanged { searchTerm ->
val currentTextLength = searchTerm?.length
    Handler().postDelayed({
        if (currentTextLength == searchTerm?.length) {
            // your code 
           Log.d("aftertextchange", "ON FINISH TRIGGER")
          }
       }, 3000)
}
Kharis Azhar
fuente