EditText コントロールを使用してリストをフィルタリングしています。ユーザーが EditText の入力を終了してから 0.5 秒後にリストをフィルター処理したいと考えています。この目的afterTextChanged
のために のイベントを使用しました。TextWatcher
ただし、このイベントは、EditText で文字が変更されるたびに発生します。
私は何をすべきか?
EditText コントロールを使用してリストをフィルタリングしています。ユーザーが EditText の入力を終了してから 0.5 秒後にリストをフィルター処理したいと考えています。この目的afterTextChanged
のために のイベントを使用しました。TextWatcher
ただし、このイベントは、EditText で文字が変更されるたびに発生します。
私は何をすべきか?
使用する:
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 (e.g., manipulating views).
}
},
DELAY
);
}
}
);
秘訣は、Timer
テキストが変更されるたびにキャンセルして再スケジュールすることEditText
です。
遅延を設定する時間については、この投稿を参照してください。
postDelayed() メソッドでHandlerを使用することをお勧めします。Android の実装では、Timer はタスクを実行するたびに新しいスレッドを作成します。ただし、 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) {
//
}
上記の解決策はどれもうまくいきませんでした。
検索ビュー内に入力したすべての文字で TextWatcher が起動せず、進行状況を表示する方法が必要でした。つまり、UI スレッドにアクセスする必要があります。
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 に渡され、そのテキストに対して行うべき作業が呼び出されます。
このコードは、重要なユーザー入力ごとではなく、間隔ごとに 1 回だけ実行されます。
彼らが書き終わったことをどのように判断しますか? edittext がフォーカスを失うことはありますか? 次にsetOnFocusChangedListenerがあります。
問題の最新の編集への対応: 最後のキーストロークから特定の時間待機する場合は、最初のキー押下でスレッドを開始する必要があります (TextWatcher を使用)。最新のキーストロークの時刻を常に登録します。スレッドを最後のキーストロークの時間 + 0.5 秒までスリープさせます。最新のキーストロークのタイムスタンプが更新されていない場合は、意図したとおりに実行してください。
また、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 the abstract method to leave the
// implementation to the implementing class
}
アクティビティでは、次のように使用できます。
// Notice I'm passing in the constructor of CustomTextWatcher
// myEditText I needed to use
myEditText.addTextChangedListener(new CustomTextWatcher(myEditText) {
@Override
public void textWasChanged() {
//doSomething(); This is method inside your activity
}
});
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;
}
});
毎回新しいオブジェクトを作成するため、ケースにタイマーを使用することは最善の解決策ではありません。Timer documentationによると、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
}
}
);