android EditText - Eingabeereignis beendet

121

Ich möchte ein Ereignis abfangen, wenn der Benutzer die Bearbeitung von EditText abgeschlossen hat.

Wie geht das?

Eddyuk
quelle
1
@PadmaKumar Der natürlichste Weg wäre, wenn der Fokus auf dem EditText verloren geht - dann ist der Benutzer definitiv fertig, aber aus Erfahrung scheint es, dass nicht alle Android-Widgets den Fokus richtig greifen (ich hatte diese Probleme mit Spinnern und Schaltflächen) - damit andere Widgets nicht den Fokus verlieren ...
AgentKnopf
3
Warum sollten wir uns auf einer so großen Plattform mit solchen einfachen Funktionen befassen? Google wird nichts verlieren, wenn sie mindestens solche Hauptfunktionen zu ihren Support-Bibliotheken hinzufügen
MBH
Wenn Sie die genaue Länge der Zeichenfolge kennen, die der Benutzer eingeben wird, rufen Sie den Text in afterTextChanged ab. Beispiel:override fun afterTextChanged(s: Editable?) { if (s.toString().length == 5) { val enteredString = s.toString() }
Shylendra Madda
2
Ich kratzte mir am Kopf und wunderte mich, warum dies in Android so schwierig ist, dass wir (ich selbst eingeschlossen) nach einer Online-Diskussion und vielen verschiedenen Lösungen mit mehr als 10 Codezeilen suchen und diese starten müssen.
Nsandersen

Antworten:

119

Wenn der Benutzer die Bearbeitung abgeschlossen hat, drückt er DoneoderEnter

((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
quelle
50
Kannst du dir da sicher sein? Ich habe die Angewohnheit, auf das nächste bearbeitbare Feld zu tippen - ich drücke so gut wie nie die Eingabetaste / Fertig - und was ich von unseren Kunden gesehen habe, auch nicht ... Ich spreche von einer Liste von Edittexten / Comboboxen usw. Wenn Sie dem Benutzer nur ein Eingabefeld geben und ihn zwingen, eine Taste zu drücken, bevor er zu anderen Feldern
wechseln
5
Denken Sie daran, dass für Ihren Bearbeitungstext auch android: singleLine = "true" festgelegt sein muss. Dank stackoverflow.com/questions/15901863/…
Steven Smith
1
Gute Lösung, kann aber mit dem Nullzeiger abstürzen ... 'event' kann bei Done action null sein, sodass event.isShiftPressed () abstürzt. Wenn Sie dies verwenden, fügen Sie Nullzeigerprüfungen hinzu.
Georgie
Bearbeiten hinzugefügt, um auf null "Ereignis" zu prüfen.
Word Rearranger
3
Was ist, wenn Sie die Taste ZURÜCK drücken, um die Tastatur zu schließen?
user347187
183

Besser noch, Sie können auch den Listener EditText onFocusChange verwenden, um zu überprüfen, ob der Benutzer die Bearbeitung abgeschlossen hat: (Sie müssen sich nicht darauf verlassen, dass der Benutzer die Taste Fertig oder Eingabetaste auf der Softtastatur drückt. )

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

Hinweis: Für mehr als einen EditText können Sie auch Ihre Klasse implementieren lassen, View.OnFocusChangeListenerdann die Listener auf jeden von Ihnen EditText setzen und sie wie folgt validieren

((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
quelle
60
Dies funktioniert nicht, wenn nur eine fokussierbar ist EditText. Es wird niemals den Fokus verlieren.
Bart Friederichs
Was ist der beste Weg, wenn wir nur einen einzigen haben EditText? Muss ich verwenden onEditorAction?
Tux
3
Wenn es nur eine gibt EditText, überprüfen Sie, was der Benutzer tut, um den Bildschirm zu verlassen.
Christine
Ich habe es versucht, aber es hat nicht funktioniert. Jedes Mal, wenn ich die Eingabetaste drückte, erhielt ich eine ähnliche Meldung: 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 }Obwohl ich etwas eingegeben habe. Dies ist ein Edittext, der nur Zahlen akzeptiert.
ahitt6345
1
Die Verwendung des OnFocusChange-Listeners hat einen Nebeneffekt: Er wird nicht aufgerufen, wenn EditText den Fokus hat und das Gerät gedreht wird! Ich habe es gelöst, indem ich someOtherView.requestFocus () in Activities onPause () eingefügt habe. (vor super.onPause ()) Auf diese Weise ist es sicher, dass EditText den Fokus verliert und der Listener aufgerufen wird.
Allofmex
60

Ich persönlich bevorzuge die automatische Übermittlung nach dem Ende der Eingabe. So können Sie dieses Ereignis erkennen.

Erklärungen und Initialisierung:

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

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

Wenn also der Text geändert wird, wartet der Timer auf die nächsten Änderungen. Wenn sie auftreten, wird der Timer abgebrochen und dann erneut gestartet.

ZimaXXX
quelle
11
Das ist großartig, aber Vorsicht, Timer können Speicherlecks verursachen. Verwenden Sie sie Handlerstattdessen
Moh Mah,
Es ist großartig, Thansk.
VipPunkJoshers Droopy
Was ist serviceConnector?
altruistisch
@altruistic Es ist nur ein Fragment meines Codes, aus dem ich die Antwort extrudiert habe. Es ist der Ort, an den die Logik gehen sollte.
ZimaXXX
21

Sie können dies mit setOnKeyListener oder mit einem textWatcher wie dem folgenden tun:

Stellen Sie den Textwächter ein editText.addTextChangedListener(textWatcher);

Dann ruf an

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

        }
    };
Herabscheinen
quelle
27
Ich fürchte, dies ist nicht die (vollständige) Lösung - der TextWatcher wird nach jeder Bearbeitung ausgelöst - wenn Sie Hallo eingeben, wird er für h, e, l, l und o ausgelöst - er weiß nicht wirklich, wann der Benutzer "fertig" ist. . Wäre toll, wenn der TextWatcher tatsächlich wissen würde, wann das Widget den Fokus verloren hat und der Benutzer weitergezogen ist ...
AgentKnopf
4

Obwohl viele Antworten in die richtige Richtung weisen, denke ich, dass keine von ihnen antwortet, worüber der Autor der Frage nachgedacht hat. Zumindest habe ich die Frage anders verstanden, weil ich nach einer Antwort auf ein ähnliches Problem gesucht habe. Das Problem ist "Wie man erkennt, wann der Benutzer aufhört zu tippen, ohne dass er eine Taste drückt" und eine Aktion auslöst (z. B. automatische Vervollständigung). Wenn Sie dies tun möchten, starten Sie den Timer in onTextChanged mit einer Verzögerung, von der Sie annehmen würden, dass der Benutzer die Eingabe beendet hat (z. B. 500-700 ms), und brechen Sie für jeden neuen Buchstaben beim Starten des Timers den früheren ab (oder verwenden Sie zumindest eine Art von kennzeichnen, dass sie nichts tun, wenn sie ankreuzen). Hier ist ein ähnlicher Code wie der, den ich verwendet habe:

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

Beachten Sie, dass ich das laufende boolesche Flag in meiner asynchronen Task ändere (Task ruft den JSON vom Server zur automatischen Vervollständigung ab).

Denken Sie auch daran, dass dies viele Timer-Aufgaben erzeugt (ich denke, sie sind auf demselben Thread geplant, aber Sie müssten dies überprüfen), sodass es wahrscheinlich viele Verbesserungsmöglichkeiten gibt, aber dieser Ansatz funktioniert auch und unter dem Strich sollten Sie dies tun Verwenden Sie einen Timer, da kein Ereignis "Benutzer hat die Eingabe beendet" angezeigt wird.

Igor Čordaš
quelle
Können Sie bitte weitere Informationen und einen Beispielcode dazu angeben?
John Ernest Guadalupe
Der Kern des Codes ist in der Antwort enthalten. Ein ausführlicheres Beispiel finden Sie in der Antwort von ZimaXXX auf derselben Seite, die denselben Ansatz verwendet. Ich weiß nicht, welche anderen Informationen ich hinzufügen könnte. Wenn Sie angeben können, was nicht klar ist, werde ich versuchen, Details hinzuzufügen.
Igor Čordaš
4

@Reno und @Vinayak B antworten zusammen, wenn Sie die Tastatur nach der Aktion ausblenden möchten

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
quelle
3

Ein anderer Ansatz ... hier ein Beispiel: Wenn der Benutzer beim Tippen eine Verzögerung von 600-1000 ms hat, können Sie davon ausgehen, dass er gestoppt ist.

 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
quelle
2

Okay, das wird sicher zu 100% funktionieren.

Zuerst müssen Sie den Listener einrichten, wenn die Tastatur ein- oder ausgeblendet ist. Wenn die Tastatur angezeigt wird, tippt der Benutzer wahrscheinlich, andernfalls wird die Eingabe abgeschlossen.

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
quelle
1
Was ist mit mehreren Textbearbeitungen? Und das heightDiff > 100Zeug ist beängstigend imho.
Bondax
Kein Problem, es funktioniert gut. Werfen
Krit
2

Ich habe dieses Problem auf diese Weise gelöst. Ich habe Kotlin benutzt.

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

Ich hatte das gleiche Problem und wollte mich nicht darauf verlassen, dass der Benutzer Fertig oder Eingabetaste drückt.

Mein erster Versuch war, den onFocusChange-Listener zu verwenden, aber es kam vor, dass mein EditText standardmäßig den Fokus erhielt. Wenn der Benutzer eine andere Ansicht drückte, wurde der onFocusChange ausgelöst, ohne dass der Benutzer ihm jemals den Fokus zugewiesen hatte.

Die nächste Lösung hat es für mich getan, bei der onFocusChange angehängt wird, wenn der Benutzer den EditText berührt hat:

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

In meinem Fall wurde der Bildschirm nach dem Bearbeiten des Benutzers erneut gerendert, wodurch das myEditText-Objekt erneuert wurde. Wenn dasselbe Objekt beibehalten wird, sollten Sie wahrscheinlich den onFocusChange-Listener in onFocusChange entfernen, um das zu Beginn dieses Beitrags beschriebene onFocusChange-Problem zu vermeiden.

Jos
quelle
Sie stellen jedes Mal, wenn ein Berührungsereignis eintritt, einen Listener für Fokusänderungen ein, der mehrmals pro Sekunde erfolgt. Mach das nicht.
Jsonfry
0

Ich hatte das gleiche Problem beim Versuch, "Jetzt tippen" in der Chat-App zu implementieren. Versuchen Sie, EditText wie folgt zu erweitern:

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

Ich beendete sie mit dem gleichen Problem und konnte die Lösung nicht mit onEditorAction oder onFocusChange verwenden und wollte den Timer nicht ausprobieren. Ein Timer ist zu gefährlich für den Geschmack, wegen all der Threads und zu unvorhersehbar, da Sie nicht wissen, wann Ihr Code ausgeführt wird.

Die onEditorAction wird nicht abgefangen, wenn der Benutzer ohne Verwendung einer Schaltfläche abreist. Wenn Sie sie verwenden, beachten Sie bitte, dass KeyEvent null sein kann. Der Fokus ist an beiden Enden unzuverlässig. Der Benutzer kann den Fokus erhalten und verlassen, ohne Text einzugeben oder das Feld auszuwählen, und der Benutzer muss das letzte EditText-Feld nicht verlassen.

Meine Lösung verwendet onFocusChange und ein Flag, das gesetzt wird, wenn der Benutzer mit der Bearbeitung von Text beginnt, sowie eine Funktion, um den Text aus der letzten fokussierten Ansicht abzurufen, die ich bei Bedarf aufrufe.

Ich lösche nur den Fokus auf alle meine Textfelder, um den Code für die Textansicht zu verlassen. Der clearFocus-Code wird nur ausgeführt, wenn das Feld den Fokus hat. Ich rufe die Funktion in onSaveInstanceState auf, damit ich das Flag (mEditing) nicht als Status der EditText-Ansicht speichern muss und wenn auf wichtige Schaltflächen geklickt wird und wenn die Aktivität geschlossen wird.

Seien Sie vorsichtig mit TexWatcher, da es häufig aufgerufen wird. Ich verwende die Bedingung auf Fokus, um nicht zu reagieren, wenn der onRestoreInstanceState-Code Text eingibt. ich

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

Ich habe so etwas wie diese abstrakte Klasse gemacht, die anstelle des TextView.OnEditorActionListener-Typs verwendet werden kann.

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

Einfach auszulösen, um die Eingabe in EditText zu beenden

hat bei mir funktioniert, wenn du Java verwendest, konvertiere es

In 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
quelle