android EditText - закінчена подія набору тексту


122

Я хочу зафіксувати подію, коли користувач закінчує редагування EditText.

Як це можна зробити?


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

3
чому така велика платформа дасть нам справу з такими простими функціями? google нічого не втратить, якщо додати такі основні функції до своїх бібліотек підтримки
MBH

Якщо ви знаєте точну довжину користувачем рядка, який ви введете тоді, введіть текст у AfterTextChanged. Напр .:override fun afterTextChanged(s: Editable?) { if (s.toString().length == 5) { val enteredString = s.toString() }
Шилендра Мадда

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

Відповіді:


119

Після завершення редагування користувач натисне 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.isShiftPress () виходить з ладу. Якщо ви використовуєте це, додайте перевірку нульових покажчиків.
Георгі

Редагування додано, щоб перевірити наявність "Події".
Word Rearranger

3
що робити, якщо натиснути кнопку НАЗАД, яка закриває клавіатуру?
користувач347187

183

Краще, ви також можете скористатися слухачем EditText onFocusChange, щоб перевірити, чи виконав редагування користувач: (Не потрібно покладатися на те, що користувач натисне кнопку "Готово" або "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?
Тукс

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 має фокус і пристрій повернеться! Я вирішив це, помістивши деякийOtherView.requestFocus () у "Дії" на "Пауза" () (перед super.onPause ()) Таким чином, безпечно, що EditText втрачає фокус і слухач викликає.
Аллофмекс

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натомість
Мох Мах,

Чудово, Танськ.
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 запускається після кожного редагування - якщо ви введете привіт, він запустить h, e, l, l та o - він насправді не знає, коли користувач "зробив" . Було б чудово, якби TextWatcher насправді дізнався, коли віджет втратив фокус і користувач перейшов далі ...
AgentKnopf

4

Хоча багато відповідей вказують у правильному напрямку, я думаю, що жодна з них не відповідає тому, про що думав автор питання. Або принаймні я зрозумів питання по-іншому, тому що шукав відповіді на подібну проблему. Проблема полягає в тому, як "Як дізнатися, коли користувач перестає вводити текст, не натискаючи на нього кнопку", і запустити певну дію (наприклад, автоматичне завершення). Якщо ви хочете зробити це, запустіть Таймер в onTextChanged із запізненням, яке ви вважаєте, що користувач перестав вводити текст (наприклад, 500-700ms), для кожного нового листа, коли ви запустите таймер, скасуйте попередній (або принаймні використовуйте якийсь тип прапор, що коли вони галочують, вони нічого не роблять). Ось подібний код до того, що я використав:

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

Зауважте, що я змінюю запущений булевий прапор всередині мого завдання async (Завдання отримує json від сервера для автоматичного завершення).

Також майте на увазі, що це створює багато завдань з таймером (я думаю, вони заплановані на одній темі, але вам доведеться це перевірити), тому, мабуть, є багато місць для вдосконалення, але такий підхід також працює, і суть полягає в тому, що вам слід використовувати таймер, оскільки немає "Користувач перестав вводити подію"


Чи можете ви надати більше інформації та зразок коду щодо цього?
Джон Ернест Гуадалупе

Суть коду є у відповіді, для більш детального прикладу дивіться відповідь ZimaXXX на цій самій сторінці, яка використовує той самий підхід. Я не знаю, яку ще інформацію я можу додати, якщо ви можете вказати те, що не зрозуміло, я спробую додати деталі.
Ігор Чордаш

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речі страшно імхо.
Бондакс

Немає проблем, працює чудово. Подивіться на цей stackoverflow.com/questions/2150078/…
Кріт

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

У мене була така ж проблема, і я не хотів покладатися на те, що користувач натискає «Готово» або «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

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 може бути недійсним. Фокус недостовірний з обох кінців, користувач може отримати фокус і вийти, не вводячи жодного тексту чи вибираючи поле, і користувачеві не потрібно залишати останнє поле 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.