android EditText - zakończono wpisywanie zdarzenia

122

Chcę przechwycić zdarzenie, gdy użytkownik zakończy edycję EditText.

Jak można to zrobić?

eddyuk
źródło
1
@PadmaKumar Najbardziej naturalnym sposobem byłoby, gdyby fokus został utracony w EditText - wtedy użytkownik jest zdecydowanie skończony, jednak z doświadczenia wynika, że ​​nie wszystkie widżety Androida prawidłowo przechwytują fokus (miałem te problemy z Spinnerami i przyciskami) - więc inne widżety mogą nie stracić ostrości ...
AgentKnopf
3
dlaczego tak duża platforma pozwala nam zajmować się tego rodzaju prostymi funkcjami? Google nic nie straci, jeśli przynajmniej doda takie główne funkcje do swoich bibliotek wsparcia
MBH
Jeśli znasz dokładną długość ciągu, który użytkownik ma wprowadzić, pobierz tekst w afterTextChanged. Np .:override fun afterTextChanged(s: Editable?) { if (s.toString().length == 5) { val enteredString = s.toString() }
Shylendra Madda
2
Drapię się w głowę, zastanawiając się, dlaczego jest to tak trudne w Androidzie, że musimy (łącznie ze mną) wyszukiwać i rozpoczynać dyskusję online i wiele różnych rozwiązań z ponad 10 liniami kodu ..?
nsandersen

Odpowiedzi:

118

Gdy użytkownik zakończy edycję, naciśnie DonelubEnter

((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
źródło
50
Czy możesz być tego pewien? Mam zwyczaj wchodzenia do / do następnego edytowalnego pola - prawie nigdy nie wciskam Enter / Done - i to, co widziałem od naszych klientów, oni też nie ... Mówię o liście Edittexts / Comboboxes itp. . Jeśli dasz użytkownikowi tylko jedno pole wprowadzania i zmusisz go do naciśnięcia przycisku, zanim będzie mógł przejść do innych pól, to oczywiście inna historia ...
AgentKnopf
5
Pamiętaj, że musisz również ustawić android: singleLine = "true" dla tekstu do edycji. Dzięki stackoverflow.com/questions/15901863/…
steven smith
1
Dobre rozwiązanie, ale może się zawiesić przy pustym wskaźniku ... 'zdarzenie' może mieć wartość null w przypadku akcji Gotowe, więc event.isShiftPressed () ulega awarii. Jeśli używasz tej opcji, dodaj sprawdzanie wskaźnika zerowego.
Georgie
Dodano edycję sprawdzającą, czy „Wydarzenie” jest puste.
Zmiana kolejności słów
3
co jeśli wciśniesz przycisk WSTECZ, który zamyka klawiaturę?
user347187
183

W lepszy sposób możesz również użyć odbiornika EditText onFocusChange, aby sprawdzić, czy użytkownik dokonał edycji: (Nie musisz polegać na naciśnięciu przez użytkownika przycisku Gotowe lub Enter na klawiaturze miękkiej)

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

Uwaga: W przypadku więcej niż jednego EditText możesz również pozwolić swojej klasie zaimplementować, View.OnFocusChangeListenera następnie ustawić detektory dla każdego z was EditText i zweryfikować je, jak poniżej

((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
źródło
60
To nie działa, jeśli jest tylko jeden, na który można ustawić ostrość EditText. Nigdy nie straci ostrości.
Bart Friederichs
Jaki jest najlepszy sposób, jeśli mamy tylko jednego EditText? Czy muszę używać onEditorAction?
Tux
3
Jeśli jest tylko jeden EditText, potwierdzasz wszystko, co użytkownik robi, aby opuścić ekran.
Christine
Próbowałem tego, ale to nie zadziałało. Za każdym razem, gdy naciskałem enter, otrzymywałem komunikat podobny do tego: 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 }Mimo że coś wpisałem. To jest plik edittext, który akceptuje tylko liczby.
ahitt6345
1
Istnieje punkt boczny używania nasłuchiwania OnFocusChange: nie zostanie wywołany, jeśli EditText ma fokus, a urządzenie zostanie obrócone! Rozwiązałem to, umieszczając someOtherView.requestFocus () w Działania onPause (). (przed super.onPause ()) W ten sposób jest bezpieczne, że EditText straci fokus i zostanie wywołany listener.
allofmex
60

Osobiście wolę automatyczne przesyłanie po zakończeniu pisania. Oto, jak możesz wykryć to zdarzenie.

Deklaracje i inicjalizacja:

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

Odbiornik w np. 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);
            }
        }
    });

Tak więc, kiedy tekst jest zmieniany, licznik czasu zaczyna czekać na jakiekolwiek następne zmiany. Kiedy się pojawią, licznik czasu zostaje anulowany, a następnie uruchomiony ponownie.

ZimaXXX
źródło
11
to jest niesamowite, ale uważaj, że liczniki czasu mogą powodować wycieki pamięci, więc użyj Handlerzamiast nich
Moh Mah
To świetnie, Thansk.
VipPunkJoshers Droopy
co to jest serviceConnector?
altruistyczny
@altruistic To tylko fragment mojego kodu, z którego wyciągnąłem odpowiedź. To miejsce, w którym powinna iść logika.
ZimaXXX
21

Możesz to zrobić za pomocą setOnKeyListener lub używając textWatchera, takiego jak:

Ustaw obserwator tekstu editText.addTextChangedListener(textWatcher);

Wtedy zadzwoń

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

        }
    };
ShineDown
źródło
27
Obawiam się, że to nie jest (kompletne) rozwiązanie - textWatcher uruchamia się po każdej edycji - jeśli wpiszesz hello, będzie odpalał przez h, e, l, l oraz o - tak naprawdę nie wie, kiedy użytkownik jest „gotowy” . Byłoby wspaniale, gdyby TextWatcher faktycznie wiedział, kiedy widżet stracił fokus, a użytkownik ruszył dalej ...
AgentKnopf
4

Chociaż wiele odpowiedzi wskazuje we właściwym kierunku, myślę, że żadna z nich nie odpowiada na to, o czym myślał autor pytania. A przynajmniej inaczej zrozumiałem pytanie, ponieważ szukałem odpowiedzi na podobny problem. Problem polega na tym, że „Jak się dowiedzieć, kiedy użytkownik przestaje pisać bez naciskania przycisku” i uruchamia jakąś akcję (na przykład autouzupełnianie). Jeśli chcesz to zrobić, uruchom Timer w onTextChanged z opóźnieniem, które uważasz, że użytkownik przestał pisać (na przykład 500-700 ms), dla każdej nowej litery, gdy uruchamiasz licznik czasu, anuluj wcześniejszą (lub przynajmniej użyj jakiegoś rodzaju zaznacz, że kiedy zaznaczają, nic nie robią). Oto kod podobny do tego, którego użyłem:

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

Zauważ, że modyfikuję uruchomioną flagę logiczną w moim zadaniu asynchronicznym (zadanie pobiera plik json z serwera w celu automatycznego ukończenia).

Pamiętaj również, że tworzy to wiele zadań timera (myślę, że są one zaplanowane w tym samym wątku, ale musiałbyś to sprawdzić), więc prawdopodobnie jest wiele miejsc do poprawy, ale to podejście również działa i najważniejsze jest to, że powinieneś użyj timera, ponieważ nie ma zdarzenia „Użytkownik przestał pisać”

Igor Čordaš
źródło
Czy możesz podać więcej informacji i przykładowy kod na ten temat?
John Ernest Guadalupe
Istota kodu znajduje się w odpowiedzi, bardziej szczegółowy przykład znajduje się w odpowiedzi ZimaXXX na tej samej stronie, która używa tego samego podejścia. Nie wiem, jakie jeszcze informacje mógłbym dodać, jeśli możesz określić, które nie są jasne, spróbuję dodać szczegóły.
Igor Čordaš
4

zarówno @Reno, jak i @Vinayak B odpowiadają razem, jeśli chcesz ukryć klawiaturę po wykonaniu czynności

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
źródło
3

Inne podejście ... oto przykład: jeśli użytkownik ma opóźnienie 600-1000 ms podczas pisania, możesz uznać, że został zatrzymany.

 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
źródło
2

Okej, to na pewno zadziała w 100%.

Najpierw musisz skonfigurować odbiornik, jeśli klawiatura jest pokazywana lub ukrywana. Jeśli klawiatura jest wyświetlana, prawdopodobnie użytkownik pisze, w przeciwnym razie pisze.

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
źródło
1
a co z wieloma edycjami tekstu? I to heightDiff > 100jest przerażające imho.
Bondax
Nie ma problemu, działa dobrze. Spójrz na ten stackoverflow.com/questions/2150078/ ...
Krit
2

W ten sposób rozwiązałem ten problem. Użyłem 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
źródło
0

Miałem ten sam problem i nie chciałem polegać na tym, że użytkownik naciśnie Gotowe lub Enter.

Moją pierwszą próbą było użycie detektora onFocusChange, ale okazało się, że mój EditText ma domyślnie fokus. Gdy użytkownik nacisnął inny widok, onFocusChange został wyzwolony bez przypisania mu przez użytkownika fokusu.

Kolejne rozwiązanie zrobiło to za mnie, gdzie onFocusChange jest dołączony, jeśli użytkownik dotknął 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
                    }
                }
            }
        }
}

W moim przypadku, gdy użytkownik skończył edycję, ekran został ponownie wyrenderowany, odnawiając w ten sposób obiekt myEditText. Jeśli ten sam obiekt jest zachowany, prawdopodobnie powinieneś usunąć detektor onFocusChange w onFocusChange, aby zapobiec problemowi onFocusChange opisanemu na początku tego postu.

Jos
źródło
Ustawiasz odbiornik zmiany fokusu za każdym razem, gdy nastąpi zdarzenie dotykowe, co będzie miało miejsce kilka razy na sekundę. Nie rób tego.
jsonfry
0

Miałem ten sam problem, gdy próbowałem zaimplementować „teraz pisanie” w aplikacji czatu. spróbuj rozszerzyć EditText w następujący sposób:

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
źródło
0

Skończyłem ją z tym samym problemem i nie mogłem użyć rozwiązania z onEditorAction lub onFocusChange i nie chciałem wypróbować timera. Licznik czasu jest zbyt niebezpieczny, by smakować, ze względu na wszystkie wątki i zbyt nieprzewidywalny, ponieważ nie wiesz, kiedy kod jest wykonywany.

Operacja onEditorAction nie przechwytuje, gdy użytkownik wychodzi bez użycia przycisku, a jeśli go używasz, zauważ, że KeyEvent może mieć wartość null. Fokus jest zawodny z obu stron, użytkownik może go aktywować i wyjść bez wprowadzania tekstu lub wybierania pola, a użytkownik nie musi opuszczać ostatniego pola EditText.

Moje rozwiązanie wykorzystuje onFocusChange i flagę ustawioną, gdy użytkownik rozpoczyna edycję tekstu, oraz funkcję pobierającą tekst z ostatniego aktywnego widoku, który wywołuję w razie potrzeby.

Po prostu wyczyściłem fokus na wszystkich moich polach tekstowych, aby uchwycić kod widoku pozostawionego tekstu. Kod clearFocus jest wykonywany tylko wtedy, gdy pole ma fokus. Wywołuję funkcję w onSaveInstanceState, więc nie muszę zapisywać flagi (mEditing) jako stan widoku EditText i po kliknięciu ważnych przycisków i zamknięciu działania.

Bądź ostrożny z TexWatcherem, ponieważ jest on często wywoływany, używam warunku na fokusie, aby nie reagować, gdy kod onRestoreInstanceState wprowadza tekst. ja

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
źródło
0

Zrobiłem coś takiego jak ta abstrakcyjna klasa, której można użyć zamiast typu 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
źródło
0

Proste wywołanie zakończenia pisania w EditText

pracował dla mnie, jeśli używasz java przekonwertuj go

W Kotlinie

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