Быстрые нажатия (щелчки) на RecyclerView открывают несколько фрагментов
Я внедрил прослушиватель onClick в свой ViewHolder для моего RecyclerView
Но когда я выполняю очень быстрые двойные нажатия или щелчки мыши, он выполняет задачу (открывает отдельный фрагмент в этом случае) два или три раза.
вот мой код
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
}
}
Любые идеи о том, как предотвратить такое поведение?
Ответы
Ответ 1
Вы можете изменить его так.
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
}
}
Ответ 2
Самый простой подход здесь будет использовать setMotionEventSplittingEnabled(false)
в вашем RecyclerView
.
По умолчанию для параметра RecyclerView
установлено значение true, что позволяет обрабатывать несколько касаний.
Если установлено значение false, этот метод ViewGroup
предотвращает получение детьми RecyclerView
нескольких кликов, а только обработку первого.
Подробнее об этом здесь.
Ответ 3
Это очень неприятное поведение. Я должен использовать дополнительный флаг, чтобы предотвратить это в моей работе.
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
}
}
Ответ 4
- Создайте логическую переменную в адаптере
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
}
}
}
- Добавьте метод setCanStart в класс адаптера:
public void setCanStart(boolean can){
canStart = can;
}
- Наконец, во фрагменте или "Деятельности" (где адаптер назначен представлению реселлера) добавьте этот onResume()
@Override
public void onResume() {
super.onResume();
mAdapter.setCanStart(true);
}
Надеюсь, это поможет :)
Ответ 5
Если вы используете Kotlin, вы можете пойти с этим на основе ответа денег
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()
Затем сделайте следующее в вашем bind
name.setOnClickListener { codeThrottle.throttle { listener.onCustomerClicked(customer, false) } }
Поместите любой код, который вам нужен, вместо
listener.onCustomerClicked(customer, false)
Ответ 6
Добавьте в свою тему атрибуты
<item name="android:splitMotionEvents">false</item>
<item name="android:windowEnableSplitTouch">false</item>
Это предотвратит одновременное нажатие нескольких кранов.
Ответ 7
Я повторно использовал 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 }
}
}
Ответ 8
Вы можете сделать класс, реализующий 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)));