Подтвердить что ты не робот

Как я могу что-то сделать, через 0,5 секунды после изменения текста в моем EditText?

Я фильтрую свой список, используя EditText. Я хочу отфильтровать список через 0,5 секунды после того, как пользователь закончил вводить текст в EditText. Для этой цели я использовал afterTextChanged событие TextWatcher. Но это событие возрастает для каждого изменения персонажа в EditText.

Что мне делать?

4b9b3361

Ответ 1

editText.addTextChangedListener(
    new TextWatcher() {
        @Override public void onTextChanged(CharSequence s, int start, int before, int count) { }
        @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { }

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

        @Override
        public void afterTextChanged(final Editable s) {
            timer.cancel();
            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
                    }
                }, 
                DELAY
            );
        }
    }
);

Хитрость заключается в отмене и перепланировании Timer каждый раз, когда текст в EditText изменяется. Удачи!

ОБНОВЛЕНИЕ Для тех, кто заинтересован в том, как долго установить задержку, см. Этот пост.

Ответ 2

Лучше использовать Handler с методом postDelayed(). В реализации андроида таймер будет создавать новый поток каждый раз для запуска задачи. Однако у Handler есть свой Looper, который может быть прикреплен к любому потоку, который мы желаем, поэтому мы не будем платить дополнительную плату за создание потока.

Пример

 Handler handler = new Handler(Looper.getMainLooper() /*UI thread*/);
 Runnable workRunnable;
 @Override public void afterTextChanged(Editable s) {
    handler.removeCallbacks(workRunnable);
    workRunnable = () -> doSmth(s.toString());
    handler.postDelayed(workRunnable, 500 /*delay*/);
 }

 private final void doSmth(String str) {
    //
 }

Ответ 3

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

RxTextView.textChanges(editTextVariableName)
            .debounce(500, TimeUnit.MILLISECONDS)
            .subscribe(new Action1<String>() {
                @Override
                public void call(String value) {
                    // do some work with the updated text
                }
            });

http://reactivex.io/documentation/operators/debounce.html

Ответ 4

Для меня не было ничего из этого решения.

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

private final TextWatcher textWatcherSearchListener = new TextWatcher() {
    final android.os.Handler handler = new android.os.Handler();
    Runnable runnable;

    public void onTextChanged(final CharSequence s, int start, final int before, int count) {
        handler.removeCallbacks(runnable);
    }

    @Override
    public void afterTextChanged(final Editable s) {
        //show some progress, because you can access UI here
        runnable = new Runnable() {
            @Override
            public void run() {
                //do some work with s.toString()
            }
        };
        handler.postDelayed(runnable, 500);
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
};

Удаление обработчика на каждом onTextChanged (который вызывается, когда пользователь вводит новый символ). afterTextChanged вызывается после того, как текст был изменен во внутреннем поле ввода, где мы можем запустить новый Runnable, но отменим его, если пользователь наберет больше символов (для получения дополнительной информации, когда вызываются эти обратные вызовы, см. это). Если пользователь больше не вводит символы, интервал пройдет в postDelayed, и он вызовет работу, которую вы должны сделать с этим текстом.

Этот код будет запускаться только один раз за интервал, а не для каждого входа пользователя. Надеюсь, это поможет кому-то в будущем.

Ответ 5

Как вы определяете, что они закончили писать? Что edittext теряет фокус? Затем setOnFocusChangedListener.

Отвечаем на последние изменения. Если вы хотите подождать определенное время после последнего нажатия клавиши, тогда вам нужно запустить поток при первом нажатии клавиши (используйте TextWatcher). Постоянно регистрируйте время последнего нажатия клавиши. Пусть поток спящий во время последнего нажатия клавиши + 0,5 секунды. Если временная метка последнего нажатия клавиши не была обновлена, сделайте все, что вы намеревались.

Ответ 6

Вы также можете использовать интерфейс TextWatcher и создать свой собственный класс, который реализует его для многократного повторного использования вашего CustomTextWatcher, а также вы можете передавать представления или все, что вам может понадобиться, в его конструктор:

public abstract class CustomTextWatcher implements TextWatcher { //Notice abstract class so we leave abstract method textWasChanged() for implementing class to define it

    private final TextView myTextView; //Remember EditText is a TextView so this works for EditText also


    public AddressTextWatcher(TextView tView) { //Notice I'm passing a view at the constructor, but you can pass other variables or whatever you need
        myTextView= tView;

    }

    private Timer timer = new Timer();
    private final int DELAY = 500; //milliseconds of delay for timer

    @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(final Editable s) {
        timer.cancel();
        timer = new Timer();

        timer.schedule(

                new TimerTask() {
                    @Override
                    public void run() {
                        textWasChanged();
                    }
                },
                DELAY

        );
    }

    public abstract void textWasChanged(); //Notice abstract method to leave implementation to implementing class

}

Теперь в своей деятельности вы можете использовать это так:

    myEditText.addTextChangedListener(new CustomTextWatcher(myEditText) { //Notice I'm passing in constructor of CustomTextWatcher myEditText I needed to use
        @Override
        public void textWasChanged() {
            //doSomething(); this is method inside your activity
        }
    });

Ответ 7

это событие во время и после окончания ввода... добавьте textWatcher и в метод onTextChanged поместите:

if (charSequence.length() > 0){//your code }

Ответ 8

Если вы хотите пропустить textWatcher только в первый раз, добавьте следующий код: Это позволит textWatcher вносить любые изменения со второго раза.

    Boolean firstchange=false;
    profileEmailEditText.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) {
                    if (firstchange) {
                        emailAlertText.setVisibility(View.VISIBLE);
                    }
                    else {
                        firstchange=true;
                    }
                }

                @Override
                public void afterTextChanged(Editable s) {

                }
            });

Ответ 9

В Kotlin Language вы можете сделать это

tv_search.addTextChangedListener(mTextWatcher)

private val mTextWatcher: TextWatcher = object : TextWatcher {
        private var timer = Timer()
        private val DELAY: Long = 1000

        override fun afterTextChanged(s: Editable?) {
            timer.cancel();
            timer = Timer()
            timer.schedule(object : TimerTask() {
                override fun run() {
                         //DO YOUR STUFF HERE
                }
            }, 1000)
        }

        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
        }

        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
        }

    }

Ответ 10

С функциями расширения Kotlin и сопрограммами:

fun AppCompatEditText.afterTextChangedDebounce(delayMillis: Long, input: (String) -> Unit) {
var lastInput = ""
var debounceJob: Job? = null
val uiScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
this.addTextChangedListener(object : TextWatcher {
    override fun afterTextChanged(editable: Editable?) {
        if (editable != null) {
            val newtInput = editable.toString()
            debounceJob?.cancel()
            if (lastInput != newtInput) {
                lastInput = newtInput
                debounceJob = uiScope.launch {
                    delay(delayMillis)
                    if (lastInput == newtInput) {
                        input(newtInput)
                    }
                }
            }
        }
    }

    override fun beforeTextChanged(cs: CharSequence?, start: Int, count: Int, after: Int) {}
    override fun onTextChanged(cs: CharSequence?, start: Int, before: Int, count: Int) {}
})}

Ответ 11

Вы можете использовать таймер, после ввода текста он будет ждать 600 мс. Поместите код внутри afterTextChanged(), используя задержку в 600 мс.

@Override
    public void afterTextChanged(Editable arg0) {
        // user typed: start the timer
        timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                // do your actual work here
               editText.setText(et.getText().toString());

            }
        }, 600); // 600ms delay before the timer executes the „run" method from TimerTask
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        // nothing to do here
    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        // user is typing: reset already started timer (if existing)
        if (timer != null) {
            timer.cancel();
        }
    }
};

Ответ 12

Вы можете использовать EditorActionListener для этой цели.

editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
    @Override
    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
        if (actionId == EditorInfo.IME_ACTION_DONE) {
            //Do something here
            return true;
        }
        return false;
    }
});

Ответ 13

Использование таймера для вашего случая - не лучшее решение из-за создания нового объекта каждый раз. Согласно документации Timer (http://developer.android.com/reference/java/util/Timer.html), лучше использовать ScheduledThreadPoolExecutor -

"Таймеры назначают однократные или повторяющиеся задачи для выполнения. ScheduledThreadPoolExecutor для нового кода."

Вот лучший подход

Runnable runnabledelayedTask = new Runnable(){
    @Override
    public void run(){
        //TODO perform any operation here
    }
};

editText.addTextChangedListener(
    new TextWatcher() {
        @Override public void onTextChanged(CharSequence s, int start, int before, int count) { }
        @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { }

        private final long DELAY = 500; // milliseconds

        @Override
        public void afterTextChanged(final Editable s) {
        ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(1);
        ScheduledFuture sf = scheduledPool.schedule(callabledelayedTask, DELAY, TimeUnit.MILLISECONDS);
        //you can cancel ScheduledFuture when needed
        }
    }
);