どうやって始まったの
もう一度、レガシーコードを掘り下げ、コンテキストリークに苦労して、アプリケーションのボタンのダブルクリックロックを解除しました。私は自分が破ったものとその実装方法を正確に探す必要がありました。基本的にそのようなロックの場合、UI要素自体を無効にするか、以降のクリックを短時間無視することが提案されているという事実を考えると、既存の解決策は私にとってかなり興味深いように見えました。リンクコード。しかし、それでもボタンのリストを作成し、サポートするコードを大量に記述する必要がありました。要素のリストを格納するクラスのインスタンスを作成し、それを入力して、各クリックハンドラーで3つのメソッドを呼び出します。一般に、何かを忘れたり混乱したりする可能性のある場所はたくさんあります。そして、私は何も覚えたくありません。何かを覚えているように思えるたびに私が間違って覚えているか、誰かがすでに別の方法でやり直していること。
これを行うのが正しいのか、再インターラブルハンドラをバックグラウンドフローに効率的に転送するだけでよいのかという質問は省きます。もう少し自転車を作ります。もう少し快適かもしれません。
一般的に、自然な怠惰が私に考えさせられましたが、これなしで済ますことは可能ですか?さて、ボタンをブロックして忘れてください。そして、それはそれがあるべきように機能し続けます。最初は、おそらく接続可能なライブラリがすでに存在し、sdelayMneHorosho()という1つのタイプのメソッドのみを呼び出す必要があると思っていました。
繰り返しますが、私は古い学校のある意味で男なので、あらゆる種類の不要な中毒が好きではありません。ライブラリーとコード生成の動物園は私を失望させ、人類に失望させます。まあ、表面グーグルはタイマーまたはそれらのバリエーションを持つ典型的なオプションのみを見つけました。
例:
One
Twoなど
...
また、ハンドラの最初の行で要素をオフにしてから、オンにすることもできます。唯一の問題は、これが常に簡単に発生するとは限らないことです。この場合、ボタンが引き起こす可能性のあるすべてのオプションの最後に「電源投入」コードへの呼び出しを追加する必要があります。私がそのような解決策をその場でグーグルしなかったのは当然のことです。それらは非常に複雑であり、それらを維持することは非常に困難です。
私はそれをより簡単に、より普遍的にすることを望んでおり、それができる限り少ないことを覚えていました。
プロジェクトからのソリューション
すでに述べたように、既存のソリューションには興味深い点がありましたが、既存のソリューションの欠点がすべてありました。少なくとも、別の単純なクラスでした。彼はまた、切断されたアイテムのさまざまなリストを作成することを許可しましたが、これが理にかなっているかどうかはわかりません。
ダブルクリックをブロックするための元のクラス
:
public class MultiClickFilter {
private static final long TEST_CLICK_WAIT = 500;
private ArrayList<View> buttonList = new ArrayList<>();
private long lastClickMillis = -1;
// User is responsible for setting up this list before using
public ArrayList<View> getButtonList() {
return buttonList;
}
public void lockButtons() {
lastClickMillis = System.currentTimeMillis();
for (View b : buttonList) {
disableButton(b);
}
}
public void unlockButtons() {
for (View b : buttonList) {
enableButton(b);
}
}
// function to help prevent execution of rapid multiple clicks on drive buttons
//
public boolean isClickedLately() {
return (System.currentTimeMillis() - lastClickMillis) < TEST_CLICK_WAIT; // true will block execution of button function.
}
private void enableButton(View button) {
button.setClickable(true);
button.setEnabled(true);
}
private void disableButton(View button) {
button.setClickable(false);
button.setEnabled(false);
}
}
:
public class TestFragment extends Fragment {
<======= ========>
private MultiClickFilter testMultiClickFilter = new MultiClickFilter();
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
<======= ========>
testMultiClickFilter.getButtonList().add(testButton);
testMultiClickFilter.getButtonList().add(test2Button);
<======= ========>
testButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (testMultiClickFilter.isClickedLately()) {
return;
}
testMultiClickFilter.lockButtons();
startTestPlayback(v);
testMultiClickFilter.unlockButtons();
}
});
test2Button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (testMultiClickFilter.isClickedLately()) {
return;
}
testMultiClickFilter.lockButtons();
loadTestProperties(v);
testMultiClickFilter.unlockButtons();
}
});
<======= ========>
}
<======= ========>
}
クラスは小さく、原則としてそれが何をするかは明らかです。簡単に言うと、アクティビティまたはフラグメントのボタンをロックするには、MultiClickFilterクラスのインスタンスを作成し、ブロックする必要のあるUI要素をそのリストに入力する必要があります。複数のリストを作成できますが、この場合、各要素のハンドラーは、プルする「clickfilter」のインスタンスを「認識」している必要があります。
また、クリックを無視することはできません。これを行うには、要素のリスト全体をブロックする必要があるため、ロックを解除する必要があります。これにより、各ハンドラーに追加のコードが追加されます。この例では、unlockButtonsメソッドをfinallyブロックに配置しますしかし、あなたは決して知りません...一般的に、この決定は疑問を投げかけます。
新しいソリューション
一般的に、おそらく何らかの銀の弾丸がないことを認識して、以下が最初の前提として受け入れられました:
- ブロックされたボタンのリストを分離することはお勧めできません。そうですね、そのような分離を必要とする例は思いつきませんでした。
- 要素を無効にしないで(有効/クリック可能)、アニメーションと一般的に要素の活性を保持します。
- これを目的としたハンドラーでクリックをブロックします。適切なユーザーが機関銃のような場所をクリックしないことが想定されており、偶発的な「チャター」を防ぐには、「すべての人のために」数百ミリ秒間クリックの処理をオフにするだけで十分です。
したがって、理想的には、すべての処理が行われるコード内の1つのポイントと、任意のハンドラー内のプロジェクトの任意の場所からジャークし、繰り返されるクリックの処理をブロックする1つのメソッドが必要です。 UIが、ユーザーが1秒に2回以上クリックすることを意味しないと仮定します。必要でない場合は、明らかに、パフォーマンスに特別な注意を払う必要がありますが、私たちのケースは単純なので、喜びで震えている指は、再入力できない関数にアプリケーションをドロップできません。また、毎回のアクティビティから別のアクティビティへの単純な遷移のパフォーマンスを最適化したり、毎回進捗ダイアログを点滅させたりすることを心配する必要もありません。
これはすべてメインスレッドで機能するので、同期について心配する必要はありません。また、実際には、このメソッドでクリックを処理する必要があるか、それを無視する必要があるかのチェックを転送できます。まあ、可能であれば、ブロッキング間隔をカスタマイズ可能にすることは可能です。そのため、非常に悪い場合には、特定のハンドラーの間隔を増やすことができます。
出来ますか?
実装は驚くほどシンプルで簡潔でした。
:
package com.ai.android.common;
import android.os.Handler;
import android.os.Looper;
import androidx.annotation.MainThread;
public abstract class MultiClickFilter {
private static final int DEFAULT_LOCK_TIME_MS = 500;
private static final Handler uiHandler = new Handler(Looper.getMainLooper());
private static boolean locked = false;
@MainThread
public static boolean clickIsLocked(int lockTimeMs) {
if (locked)
return true;
locked = true;
uiHandler.postDelayed(() -> locked = false, lockTimeMs);
return false;
}
@MainThread
public static boolean clickIsLocked() {
return clickIsLocked(DEFAULT_LOCK_TIME_MS);
}
}
:
public class TestFragment {
<======= ========>
private ListView devicePropertiesListView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
devicePropertiesListView = view.findViewById(R.id.list_view);
devicePropertiesListView.setOnItemClickListener(this::doOnItemClick);
<======= ========>
return view;
}
private void doOnItemClick(AdapterView<?> adapterView, View view, int position, long id) {
if (MultiClickFilter.clickIsLocked(1000 * 2))
return;
<======= ========>
}
<======= ========>
}
概して 、プロジェクトにMultiClickFilterクラスを追加し、各クリックハンドラーの先頭でブロックされているかどうかを確認するだけです。
if (MultiClickFilter.clickIsLocked())
return;
クリックが処理される場合、ブロックは指定された時間(またはデフォルトで)設定されます。このメソッドを使用すると、要素のリストについて考えたり、複雑なチェックを作成したり、UI要素の可用性を手動で制御したりできなくなります。私はコメントでこの実装について議論することをお勧めします、おそらくより良いオプションがありますか?