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
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"
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.
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.
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
}
}
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.
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 }
}
}