web-dev-qa-db-ja.com

RecyclerViewをすばやくタップ(クリック)すると、複数のフラグメントが開きます

RecyclerViewのViewHolderにonClickリスナーを実装しました

しかし、非常に高速なダブルタップまたはマウスクリックを実行すると、タスクが2回または3回実行されます(この場合は別のフラグメントが開きます)。

これが私のコードです

    public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
    TextView tvTitle, tvDescription;

    public ViewHolder(View itemView) {
        super(itemView);
        itemView.setClickable(true);
        itemView.setOnClickListener(this);

        tvTitle = (TextView) itemView.findViewById(R.id.tv_title);
        tvDescription = (TextView) itemView.findViewById(R.id.tv_description);
    }

    @Override
    public void onClick(View v) {
        mListener.onClick(FRAGMENT_VIEW, getAdapterPosition()); // open FRAGMENT_VIEW
    }
}

そのような行動を防ぐ方法についてのアイデアはありますか?

14
Shifatul

このように変更できます。

public class ViewHolder extends RecyclerView.ViewHolder implements
        View.OnClickListener {
    TextView tvTitle, tvDescription;
    private long mLastClickTime = System.currentTimeMillis();
    private static final long CLICK_TIME_INTERVAL = 300;

    public ViewHolder(View itemView) {
        super(itemView);
        itemView.setClickable(true);
        itemView.setOnClickListener(this);

        tvTitle = (TextView) itemView.findViewById(R.id.tv_title);
        tvDescription = (TextView) itemView
                .findViewById(R.id.tv_description);
    }

    @Override
    public void onClick(View v) {
        long now = System.currentTimeMillis();
        if (now - mLastClickTime < CLICK_TIME_INTERVAL) {
            return;
        }
        mLastClickTime = now;
        mListener.onClick(FRAGMENT_VIEW, getAdapterPosition()); // open
                                                                // FRAGMENT_VIEW
    }
}
21
Money

ここでの最も簡単なアプローチは、RecyclerViewsetMotionEventSplittingEnabled(false)を使用することです。

デフォルトでは、これはRecyclerViewでtrueに設定されており、複数のタッチを処理できます。

Falseに設定すると、このViewGroupメソッドは、RecyclerViewの子が複数のクリックを受信するのを防ぎ、最初のクリックのみを処理します。

これについてもっと見る ここ

15
joao2fast4u

これは非常に迷惑な動作です。作業でこれを防ぐために、追加のフラグを使用する必要があります。

public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView tvTitle, tvDescription;
private boolean clicked;

public ViewHolder(View itemView) {
    super(itemView);
    itemView.setClickable(true);
    itemView.setOnClickListener(this);

    tvTitle = (TextView) itemView.findViewById(R.id.tv_title);
    tvDescription = (TextView) itemView.findViewById(R.id.tv_description);
}

@Override
public void onClick(View v) {
    if(clicked){
        return;
    }
    clicked = true;
    v.postDelay(new Runnable(){
          @Override
          public void run(View v){
              clicked = false;
          }
    },500);
    mListener.onClick(FRAGMENT_VIEW, getAdapterPosition()); // open FRAGMENT_VIEW
}
}
7
songchenwen
  • Adapterでブール変数を作成します
boolean canStart = true;
  • OnClickListenerを次のようにします
ViewHolder.dataText.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (canStart) {
            canStart = false; // do canStart false 
            // Whatever you want to do and not have run twice due to double tap
        }
    }
}
  • AdapterクラスにsetCanStartメソッドを追加します。
public void setCanStart(boolean can){
    canStart = can;
}
  • 最後に、フラグメントまたはアクティビティ(アダプタがrecyclerviewに割り当てられている場所)に、このonResume()を追加します。
@Override
    public void onResume() {
        super.onResume();
        mAdapter.setCanStart(true);
    }

それが役立つことを願っています:)

1
SHUJAT MUNAWAR

テーマに以下の属性を追加します

<item name="Android:splitMotionEvents">false</item>
<item name="Android:windowEnableSplitTouch">false</item>

これにより、同時に複数回タップするのを防ぐことができます。

1
Alex Chengalan

Kotlinを使用している場合は、Moneyの回答に基づいてこれを使用できます

class CodeThrottle {
    companion object {
        const val MIN_INTERVAL = 300
    }
    private var lastEventTime = System.currentTimeMillis()

    fun throttle(code: () -> Unit) {
        val eventTime = System.currentTimeMillis()
        if (eventTime - lastEventTime > MIN_INTERVAL) {
            lastEventTime = eventTime
            code()
        }
    }
}

ビューホルダーでこのオブジェクトを作成します

    private val codeThrottle = CodeThrottle()

次に、バインドで次のことを行います

name.setOnClickListener { codeThrottle.throttle { listener.onCustomerClicked(customer, false) } }

必要なコードを代わりに呼び出す

listener.onCustomerClicked(customer, false) 
1
Kevin

View.OnClickListenerを実装するクラスを作成できます

public class DoubleClickHelper implements View.OnClickListener {

    private long mLastClickTime = System.currentTimeMillis();
    private static final long CLICK_TIME_INTERVAL = 300;
    private Callback callback;

    public DoubleClickHelper(Callback callback) {
        this.callback = callback;
    }

    @Override
    public void onClick(View v) {
        long now = System.currentTimeMillis();
        if (now - mLastClickTime < CLICK_TIME_INTERVAL) {
            return;
        }
        mLastClickTime = now;
        callback.handleClick();
    }

    public interface Callback {
        void handleClick();
    }
}

そしてそれを次のように使用するよりも:

ivProduct.setOnClickListener(new DoubleClickHelper(() -> listener.onProductInfoClick(wItem)));
0
Ilia Grabko

DebouncingOnClickListener をButterknifeから再利用して、複数のビューでのクリックを防ぐことに加えて、指定された時間内にクリックをデバウンスしました。

使用するには、それを拡張してdoOnClickを実装します。

DebouncingOnClickListener.kt

import Android.view.View

/**
 * A [click listener][View.OnClickListener] that debounces multiple clicks posted in the
 * same frame and within a time frame. A click on one view disables all view for that frame and time
 * span.
 */
abstract class DebouncingOnClickListener : View.OnClickListener {

    final override fun onClick(v: View) {
        if (enabled && debounced) {
            enabled = false
            lastClickTime = System.currentTimeMillis()
            v.post(ENABLE_AGAIN)
            doClick(v)
        }
    }

    abstract fun doClick(v: View)

    companion object {
        private const val DEBOUNCE_TIME_MS: Long = 1000

        private var lastClickTime = 0L // initially zero so first click isn't debounced

        internal var enabled = true
        internal val debounced: Boolean
            get() = System.currentTimeMillis() - lastClickTime > DEBOUNCE_TIME_MS

        private val ENABLE_AGAIN = { enabled = true }
    }
}
0
Ryan R

これは遅れており、すでに回答が得られていることはわかっていますが、私の場合のこの同様の問題は、サードパーティのライブラリであるMaterial RippleLayoutが原因であることがわかりました。デフォルトでは、onClickへの遅延呼び出しを有効にし、onClickに対して複数の要求を行うことができるため、アニメーションが終了すると、これらのクリックがすべて一度に登録され、複数のダイアログが開きます。

この設定は遅延をキャンセルし、私の場合は問題を修正しました。

app:mrl_rippleDelayClick="false"
0
xZ4FiRx

手遅れですが、他の人のために働くことができます:

recyclerAdapter.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            int id = ....
            if(id == 1){
                view.setClickable(false); //add this
                Intent a = new Intent...
                startActivity(a);

            }else if(id == 2){
                view.setClickable(false);
                Intent b = ...
                startActivity(b);
            }
        }
    });

フラグメント-onResume()

@Override
public void onResume() {
    super.onResume();
    Objects.requireNonNull(getActivity()).invalidateOptionsMenu();
    recyclerView.setAdapter(recyclerAdapter); //add this
}

それは私のために働きます、私はそれが正しいかどうかわかりません。