Fast taps (clicks) on RecyclerView opens multiple Fragments

前端 未结 10 893
渐次进展
渐次进展 2021-02-02 13:37

I have implemented onClick listener to my ViewHolder for my RecyclerView

But when I perform very fast double taps or mouse clicks, it performs the task (opens a seperate

相关标签:
10条回答
  • 2021-02-02 13:49

    I know this is late and the answer has already been given, but I found that this similar issue for my case was due to a third party library Material Ripple Layout. By default it enables a delay call to onClick and allows for multiple request to be made to onClick so when the animation finishes all those clicks get registered at once and opens multiple dialogs.

    this setting cancels the delay and fixed the problem for me in my case.

    app:mrl_rippleDelayClick="false"
    
    0 讨论(0)
  • 2021-02-02 13:52

    The most straightforward approach here would be using setMotionEventSplittingEnabled(false) in your RecyclerView.

    By default, this is set to true in RecyclerView, allowing multiple touches to be processed.

    When set to false, this ViewGroup method prevents the RecyclerView children to receive the multiple clicks, only processing the first one.

    See more about this here.

    0 讨论(0)
  • 2021-02-02 13:57

    Add below attributes in your theme

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

    This will prevent multiple tap at same time.

    0 讨论(0)
  • 2021-02-02 14:00

    This is a very annoying behavior. I have to use an extra flag to prevent this in my work.

    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
    }
    }
    
    0 讨论(0)
  • 2021-02-02 14:00

    Too late but it can work for other people:

    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);
                }
            }
        });
    

    Fragment - onResume()

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

    It works for me, i don't know if it's correct.

    0 讨论(0)
  • 2021-02-02 14:07

    I repurposed the DebouncingOnClickListener from Butterknife to debounce clicks within a specified time, in addition to preventing clicks on multiple views.

    To use, extend it and implement 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 讨论(0)
提交回复
热议问题