android EditText - событие завершения набора текста


122

Я хочу поймать событие, когда пользователь заканчивает редактирование EditText.

Как это сделать?


1
@PadmaKumar Самым естественным способом было бы, если фокус теряется на EditText - тогда пользователь определенно готов, однако по опыту кажется, что не все виджеты Android правильно захватывают фокус (у меня были проблемы с прядильщиками и кнопками) - чтобы другие виджеты не теряли фокус ...
AgentKnopf

3
почему такая большая платформа позволяет нам заниматься такими простыми функциями? Google ничего не потеряет, если они добавят такие основные функции хотя бы в свои библиотеки поддержки
MBH

Если вы знаете точную длину строки, которую пользователь будет вводить, то получите текст в afterTextChanged. Пример:override fun afterTextChanged(s: Editable?) { if (s.toString().length == 5) { val enteredString = s.toString() }
Шилендра Мадда

2
Я ломаю голову, удивляясь, почему это так сложно в Android, что нам (в том числе и мне) нужно искать и начинать онлайн-обсуждение и множество различных решений с более чем 10 строками кода ..?
nsandersen 01

Ответы:


118

Когда пользователь закончит редактирование, он / она нажмет DoneилиEnter

((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
Ты можешь быть уверен в этом? У меня есть привычка нажимать на следующее редактируемое поле - я почти никогда не нажимаю Enter / Done - и то, что я видел от наших клиентов, они тоже ... Я говорю о списке Edittexts / Comboboxes и т. Д. .Если вы дадите пользователю только одно поле ввода и заставите его нажать кнопку, прежде чем он сможет перейти к другим полям, тогда это, очевидно, другая история ...
AgentKnopf

5
Помните, что для редактируемого текста также необходимо установить android: singleLine = "true". Благодаря stackoverflow.com/questions/15901863/…
Стивен Смит,

1
Хорошее решение, но может произойти сбой с нулевым указателем ... 'event' может быть нулевым при действии Done, поэтому event.isShiftPressed () вылетает. Если вы используете это, добавьте проверки нулевого указателя.
Джорджи

Добавлена ​​правка для проверки нулевого «Событие».
Word Rearranger

3
что если вы нажмете кнопку НАЗАД, закрывающую клавиатуру?
user347187 04

183

Лучше всего, вы также можете использовать EditText onFocusChange listener, чтобы проверить, выполнил ли пользователь редактирование: (не нужно полагаться на то, что пользователь нажимает кнопку Done или Enter на мягкой клавиатуре)

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

Примечание. Для более чем одного EditText вы также можете позволить своему классу реализовать, а View.OnFocusChangeListenerзатем установить слушателей для каждого из вас EditText и проверить их, как показано ниже.

((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
Это не работает, если есть только один объект для фокусировки EditText. Он никогда не потеряет фокус.
Барт Фридрихс

Как лучше всего, если у нас будет только сингл EditText? Я должен использовать onEditorAction?
Tux

3
Если есть только один EditText, вы проверяете, что пользователь делает, чтобы покинуть экран.
Кристина

Я пробовал это, но это не сработало. Каждый раз, когда я нажимал клавишу « 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 }Ввод», я получал примерно следующее сообщение: « Даже если я что-то ввел. Это текст редактирования, который принимает только числа.
ahitt6345

1
Есть сторона использования прослушивателя OnFocusChange: он не будет вызываться, если EditText имеет фокус и устройство поворачивается! Я решил это, поместив someOtherView.requestFocus () в Activity onPause (). (перед super.onPause ()) Таким образом, EditText теряет фокус и вызывается слушатель.
allofmex 06

60

Я лично предпочитаю автоматическую отправку после окончания набора. Вот как вы можете обнаружить это событие.

Объявления и инициализация:

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

Слушатель, например, в 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);
            }
        }
    });

Итак, когда текст изменяется, таймер начинает ждать следующих изменений. Когда они происходят, таймер отменяется, а затем запускается снова.


11
это круто, но будьте осторожны, таймеры могут вызвать утечку памяти, поэтому используйте Handlerвместо этого
Moh Mah

Это здорово, Танск.
VipPunkJoshers Droopy

что такое serviceConnector?
альтруистический

@altruistic Это просто фрагмент моего кода, из которого я извлек ответ. Это то место, куда должна идти логика.
ZimaXXX

21

Вы можете сделать это с помощью setOnKeyListener или с помощью textWatcher, например:

Установить наблюдатель текста editText.addTextChangedListener(textWatcher);

тогда позвони

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
Я боюсь, что это не (полное) решение - textWatcher срабатывает после каждого редактирования - если вы наберете hello, он сработает для h, e, l, l и o - он действительно не знает, когда пользователь "готов" , Было бы здорово, если бы TextWatcher действительно знал, когда виджет потерял фокус и пользователь перешел ...
AgentKnopf

4

Хотя многие ответы действительно указывают в правильном направлении, я думаю, что ни один из них не отвечает на то, о чем думал автор вопроса. Или, по крайней мере, я иначе понял вопрос, потому что искал ответ на похожую проблему. Проблема заключается в том, «как узнать, когда пользователь прекращает вводить текст без нажатия кнопки» и запускать какое-либо действие (например, автозаполнение). Если вы хотите сделать это, запустите таймер в onTextChanged с задержкой, которую вы считаете, что пользователь перестал печатать (например, 500-700 мс), для каждой новой буквы, когда вы запускаете таймер, отменяйте предыдущую (или, по крайней мере, используйте какой-то отметьте, что когда они ставят галочку, они ничего не делают). Вот код, аналогичный тому, что я использовал:

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

Обратите внимание, что я изменяю рабочий логический флаг внутри моей асинхронной задачи (Task получает json с сервера для автозаполнения).

Также имейте в виду, что это создает много задач таймера (я думаю, что они запланированы в одном потоке, но вам нужно это проверить), поэтому, вероятно, есть много мест, которые можно улучшить, но этот подход также работает, и суть в том, что вы должны используйте таймер, поскольку нет сообщения «Пользователь прекратил вводить сообщение»


Не могли бы вы предоставить дополнительную информацию и примеры кода по этому поводу?
Джон Эрнест Гваделупе

Суть кода находится в ответе, более подробный пример см. В ответе ZimaXXX на этой же странице, где используется тот же подход. Я не знаю, какую еще информацию я мог бы добавить, если вы можете указать, что непонятно, я постараюсь добавить подробности.
Игорь Кордаш 02

4

оба @Reno и @Vinayak B ответят вместе, если вы хотите скрыть клавиатуру после действия

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

Другой подход ... вот пример: если у пользователя есть задержка в 600-1000 мс при вводе текста, вы можете считать, что он остановился.

 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

Хорошо, это наверняка сработает на 100%.

Сначала вам нужно будет настроить прослушиватель, если клавиатура отображается или скрывается. Если отображается клавиатура, возможно, пользователь печатает, в противном случае печатает.

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
а как насчет нескольких правок текста? И heightDiff > 100штука страшная имхо.
Bondax 02

Нет проблем, работает нормально. Взгляните на этот stackoverflow.com/questions/2150078/…
Krit

2

Я решил эту проблему таким образом. Я использовал котлин.

        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

У меня была такая же проблема, и я не хотел полагаться на то, что пользователь нажимает Done или Enter.

Моя первая попытка состояла в том, чтобы использовать прослушиватель onFocusChange, но оказалось, что мой EditText получил фокус по умолчанию. Когда пользователь нажимал какое-то другое представление, onFocusChange запускался без того, чтобы пользователь никогда не назначал ему фокус.

Следующее решение сделало это для меня, где onFocusChange прикреплен, если пользователь коснулся 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
                    }
                }
            }
        }
}

В моем случае, когда пользователь закончил редактирование, экран был повторно отрисован, тем самым обновив объект myEditText. Если тот же объект сохраняется, вам, вероятно, следует удалить прослушиватель onFocusChange в onFocusChange, чтобы предотвратить проблему onFocusChange, описанную в начале этого сообщения.


Вы устанавливаете прослушиватель смены фокуса каждый раз, когда происходит событие касания, которое будет происходить несколько раз в секунду. Не делай этого.
jsonfry 07

0

У меня была такая же проблема при попытке реализовать «сейчас набираю текст» в приложении чата. попробуйте расширить EditText следующим образом:

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

Я покончил с ней с той же проблемой, и я не мог использовать решение с onEditorAction или onFocusChange и не хотел пробовать таймер. Таймер слишком опасен для вкуса из-за всех потоков и слишком непредсказуем, поскольку вы не знаете, когда ваш код выполняется.

OnEditorAction не улавливает, когда пользователь уходит без использования кнопки, и если вы его используете, обратите внимание, что KeyEvent может иметь значение null. Фокус ненадежен на обоих концах, пользователь может получить фокус и уйти, не вводя текст или не выбирая поле, и пользователю не нужно оставлять последнее поле EditText.

В моем решении используется onFocusChange и установлен флаг, когда пользователь начинает редактировать текст, и функцию для получения текста из последнего сфокусированного представления, которое я вызываю при необходимости.

Я просто снимаю фокус со всех своих текстовых полей, чтобы обмануть код представления оставленного текста. Код clearFocus выполняется только в том случае, если поле имеет фокус. Я вызываю функцию в onSaveInstanceState, поэтому мне не нужно сохранять флаг (mEditing) как состояние представления EditText и при нажатии важных кнопок и при закрытии действия.

Будьте осторожны с TexWatcher, так как он часто вызывается. Я использую условие фокуса, чтобы не реагировать, когда код onRestoreInstanceState вводит текст. я

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

Я сделал что-то вроде этого абстрактного класса, который можно использовать вместо типа 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

Легко вызвать завершение набора текста в EditText

работал у меня, если вы используете java, конвертируйте его

В Котлине

youredittext.doAfterTextChanged { searchTerm ->
val currentTextLength = searchTerm?.length
    Handler().postDelayed({
        if (currentTextLength == searchTerm?.length) {
            // your code 
           Log.d("aftertextchange", "ON FINISH TRIGGER")
          }
       }, 3000)
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.