android EditText - terminata la digitazione dell'evento


122

Voglio catturare un evento quando l'utente finisce di modificare EditText.

Come si può fare?


1
@PadmaKumar Il modo più naturale sarebbe se l'attenzione si perde su EditText - quindi l'utente ha decisamente finito, tuttavia dall'esperienza sembra che non tutti i widget Android stiano attirando correttamente l'attenzione (ho avuto quei problemi con Spinners e Buttons) - quindi altri widget potrebbero non perdere il focus ...
AgentKnopf

3
perché una piattaforma così grande ci permette di gestire questo tipo di semplici funzioni? Google non perderà nulla se aggiungerà tali funzionalità principali almeno alle loro librerie di supporto
MBH

Se conosci la lunghezza esatta della stringa che l'utente dovrà inserire, ottieni il testo in afterTextChanged. Es:override fun afterTextChanged(s: Editable?) { if (s.toString().length == 5) { val enteredString = s.toString() }
Shylendra Madda

2
Mi sto grattando la testa chiedendomi perché questo è così difficile in Android che noi (me compreso) abbiamo bisogno di cercare e avviare una discussione online e molte soluzioni diverse con 10+ righe di codice ..?
nsandersen

Risposte:


118

Quando l'utente ha terminato la modifica, premerà 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. 
        }
    }
);

50
Puoi esserne sicuro? Ho l'abitudine di toccare nel / sul campo modificabile successivo - praticamente non premo mai Invio / Fatto - e quello che ho visto dai nostri clienti, nemmeno loro ... Sto parlando di un elenco di Edittext / Combobox ecc. Se dai all'utente un solo campo di input e lo costringi a premere un pulsante prima che possa passare ad altri campi, questa è ovviamente una storia diversa ...
AgentKnopf

5
Ricorda, devi anche impostare android: singleLine = "true" per il testo di modifica. Grazie a stackoverflow.com/questions/15901863/…
steven smith

1
Buona soluzione ma può bloccarsi con un puntatore nullo ... "evento" può essere nullo all'azione completata, quindi event.isShiftPressed () si blocca. Se si utilizza questo, aggiungere controlli del puntatore nullo.
Georgie

Modifica aggiunta per verificare la presenza di "Evento" nullo.
Riorganizzatore di parole

3
cosa succede se si preme il pulsante INDIETRO che chiude la tastiera?
user347187

183

In modo migliore, puoi anche utilizzare EditText onFocusChange listener per verificare se l'utente ha effettuato la modifica: (non è necessario fare affidamento sull'utente che preme il pulsante Fine o Invio sulla tastiera morbida)

 ((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: per più di un EditText, puoi anche lasciare che la tua classe implementi View.OnFocusChangeListenerquindi impostare gli ascoltatori su ciascuno di voi EditText e convalidarli come di seguito

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

60
Questo non funziona se c'è un solo focalizzabile EditText. Non perderà mai la concentrazione.
Bart Friederichs

Qual è il modo migliore se abbiamo solo un singolo EditText? Devo usare onEditorAction?
Tux

3
Se ce n'è solo uno EditText, convalidi ciò che l'utente fa per lasciare lo schermo.
Christine

Ho provato questo ma non ha funzionato. Ogni volta che premevo Invio mi dava un messaggio simile a questo: 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 }Anche se ho digitato qualcosa. Questo è un testo di modifica che accetta solo numeri.
ahitt6345

1
C'è un aspetto secondario nell'usare il listener di OnFocusChange: non verrà chiamato se EditText è attivo e il dispositivo viene ruotato! L'ho risolto mettendo someOtherView.requestFocus () in Activities onPause (). (prima di super.onPause ()) In questo modo è sicuro che EditText perda il focus e l'ascoltatore venga chiamato.
allofmex

60

Personalmente preferisco l'invio automatico dopo la fine della digitazione. Ecco come puoi rilevare questo evento.

Dichiarazioni e inizializzazione:

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

Listener in es. 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);
            }
        }
    });

Quindi, quando il testo viene modificato, il timer inizia ad attendere che avvengano eventuali modifiche successive. Quando si verificano, il timer viene annullato e quindi riavviato.


11
questo è fantastico, ma attenzione che i timer possono causare perdite di memoria, quindi usa Handlerinvece
Moh Mah

È fantastico, Thansk.
VipPunkJoshers Droopy

cos'è serviceConnector?
altruista

@altruistic È solo un frammento del mio codice da cui ho estruso la risposta. È il luogo in cui dovrebbe andare la logica.
ZimaXXX

21

Puoi farlo usando setOnKeyListener o usando un textWatcher come:

Imposta l'osservatore di testo editText.addTextChangedListener(textWatcher);

quindi chiama

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

        }
    };

27
Temo che questa non sia la soluzione (completa) - il textWatcher si attiva dopo ogni modifica - se digiti hello si attiverà per h, e, l, le o - non sa veramente quando l'utente ha "finito" . Sarebbe fantastico se TextWatcher sapesse effettivamente quando il widget ha perso il focus e l'utente è passato a ...
AgentKnopf

4

Sebbene molte risposte puntino nella giusta direzione, penso che nessuna di esse risponda a ciò a cui stava pensando l'autore della domanda. O almeno ho capito la domanda in modo diverso perché cercavo una risposta a un problema simile. Il problema è "Come sapere quando l'utente smette di digitare senza che lui prema un pulsante" e attiva un'azione (ad esempio il completamento automatico). Se vuoi farlo, avvia il Timer in onTextChanged con un ritardo che considereresti che l'utente abbia smesso di digitare (ad esempio 500-700ms), per ogni nuova lettera quando avvii il timer annulla quella precedente (o almeno usa una sorta di segnala che quando spuntano non fanno nulla). Ecco un codice simile a quello che ho usato:

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

Si noti che modifico l'esecuzione del flag booleano all'interno della mia attività asincrona (l'attività ottiene il json dal server per il completamento automatico).

Tieni anche presente che questo crea molte attività del timer (penso che siano programmate sullo stesso thread tu ma dovresti controllarlo), quindi probabilmente ci sono molti posti per migliorare ma anche questo approccio funziona e la linea di fondo è che dovresti utilizza un timer poiché non è presente alcun "evento di digitazione interrotto dall'utente"


Puoi fornire maggiori informazioni e un codice di esempio su questo?
John Ernest Guadalupe

L'essenza del codice è nella risposta, per un esempio più dettagliato, vedere la risposta di ZimaXXX su questa stessa pagina che utilizza lo stesso approccio. Non so cos'altro potrei aggiungere, se puoi specificare cosa non è chiaro cercherò di aggiungere dettagli.
Igor Čordaš

4

sia @Reno che @Vinayak B rispondono insieme se vuoi nascondere la tastiera dopo l'azione

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

3

Un approccio diverso ... ecco un esempio: se l'utente ha un ritardo di 600-1000 ms durante la digitazione, potresti considerare che è fermo.

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


2

Va bene, funzionerà al 100% di sicuro.

Per prima cosa dovrai impostare l'ascoltatore se la tastiera è mostra o nascondi. Se la tastiera è visualizzata, probabilmente l'utente sta digitando, altrimenti digitato.

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

1
che dire delle modifiche multiple al testo? E la heightDiff > 100roba è spaventosa imho.
Bondax

Nessun problema, funziona bene. Dai un'occhiata a questo stackoverflow.com/questions/2150078/…
Krit

2

Ho risolto questo problema in questo modo. Ho usato 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.
            }
        })

0

Ho avuto lo stesso problema e non volevo fare affidamento sul fatto che l'utente premesse Fine o Invio.

Il mio primo tentativo è stato quello di utilizzare il listener onFocusChange, ma si è verificato che il mio EditText ha ottenuto il focus per impostazione predefinita. Quando l'utente premeva un'altra vista, onFocusChange veniva attivato senza che l'utente gli avesse mai assegnato il focus.

La soluzione successiva l'ha fatto per me, dove è allegato onFocusChange se l'utente ha toccato 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
                    }
                }
            }
        }
}

Nel mio caso, quando l'utente ha terminato la modifica, è stato eseguito il rendering dello schermo, rinnovando così l'oggetto myEditText. Se lo stesso oggetto viene mantenuto, dovresti probabilmente rimuovere il listener onFocusChange in onFocusChange per prevenire il problema di onFocusChange descritto all'inizio di questo post.


Stai impostando un ascoltatore che cambia focus ogni volta che si verifica un evento di tocco, che sarà diverse volte al secondo. Non farlo.
jsonfry

0

Ho avuto lo stesso problema durante il tentativo di implementare "digitazione in corso" nell'app di chat. prova ad estendere EditText come segue:

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

}


0

L'ho terminata con lo stesso problema e non potevo usare la soluzione con onEditorAction o onFocusChange e non volevo provare il timer. Un timer è troppo pericoloso perché può avere un sapore, a causa di tutti i thread e troppo imprevedibile, poiché non sai quando il codice viene eseguito.

L'azione onEditorAction non si cattura quando l'utente esce senza utilizzare un pulsante e se lo si utilizza si prega di notare che KeyEvent può essere nullo. Lo stato attivo è inaffidabile ad entrambe le estremità l'utente può ottenere lo stato attivo e uscire senza inserire alcun testo o selezionare il campo e l'utente non deve lasciare l'ultimo campo EditText.

La mia soluzione usa onFocusChange e un flag impostato quando l'utente inizia a modificare il testo e una funzione per ottenere il testo dall'ultima vista focalizzata, che chiamo quando necessario.

Ho solo cancellato il focus su tutti i miei campi di testo per ingannare il codice di visualizzazione del testo di uscita, Il codice clearFocus viene eseguito solo se il campo è attivo. Chiamo la funzione in onSaveInstanceState quindi non devo salvare il flag (mEditing) come stato della vista EditText e quando si fa clic sui pulsanti importanti e quando l'attività è chiusa.

Fai attenzione con TexWatcher poiché è chiamata spesso uso la condizione su focus per non reagire quando il codice onRestoreInstanceState inserisce del testo. io

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

0

Ho fatto qualcosa di simile a questa classe astratta che può essere utilizzata al posto 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
}

0

Semplice per attivare la fine della digitazione in EditText

ha funzionato per me, se usi java convertilo

A Kotlin

youredittext.doAfterTextChanged { searchTerm ->
val currentTextLength = searchTerm?.length
    Handler().postDelayed({
        if (currentTextLength == searchTerm?.length) {
            // your code 
           Log.d("aftertextchange", "ON FINISH TRIGGER")
          }
       }, 3000)
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.