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

122

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

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

eddyuk
источник
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. 
        }
    }
);
Reno
источник
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);
            }
        }
    });

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

ZimaXXX
источник
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;
                        }
                    }
            }
        });
Krit
источник
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, описанную в начале этого сообщения.

Jos
источник
Вы устанавливаете прослушиватель смены фокуса каждый раз, когда происходит событие касания, которое будет происходить несколько раз в секунду. Не делай этого.
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();
    }
}
Rezder
источник
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)
}
Харис Ажар
источник