Предотвращение использования RecyclerView для использования событий касания
Проблема
Я пытаюсь смоделировать точное поведение приложения Google Play.
В настоящее время, когда вы прокручиваете категорию, например "Лучшие игры", и вы касаетесь списка, эта ячейка, а также RecyclerView
обрабатывает событие касания, вы можете сказать, что они оба обрабатывают ее из-за появляющейся ряби когда вы держите палец вниз, а также прокручиваетесь, останавливаясь. Или если вы нажмете на ячейку, пока список все еще перемещается, содержимое сразу открывается.
Поведение по умолчанию заключается в том, что прокрутка мгновенно останавливается, если вы поместите свой палец вниз, пока он двигается, и для этого он должен использовать это событие касания, то есть клетка никогда не получает об этом касании. Это невероятно раздражает, когда основное содержимое вашего приложения находится в RecyclerView
, и если пользователь постоянно прокручивает, а затем щелкает по ячейке, он требует, чтобы они дважды щелкнули, поскольку первый клик был поглощен остановкой прокрутки, даже если список едва двигался.
Что я пробовал до сих пор
Сначала я подумал, что внутри RecyclerView
должен быть какой-то флаг, который говорит ему, чтобы он не использовал событие касания. Я просмотрел исходный код Android и направился через документацию по Android безрезультатно.
Затем я попытался захватить событие касания с помощью onInterceptTouchEvent
и вручную сообщить своему представлению, что он был затронут;
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
View myCell = recyclerView.findChildViewUnder(event.getX(), event.getY());
myCell.dispatchTouchEvent(event);
return super.onInterceptTouchEvent(event);
}
Здесь dispatchTouchEvent
, похоже, ничего не сделал, хотя представление, которое я получал, имело соответствующий тег, который я установил в адаптере.
Я пробовал снимать эту проблему с помощью своего пулемета кода под любым углом; играя с requestDisallowInterceptTouchEvent
, вручную вызывая onTouch
и переопределяя все способы прослушивания для RecyclerView
и далее.
Кажется, что ничего не работает. Я уверен, что должно быть элегантное решение, и я просмотрел важную деталь где-то на пути к безумию? Тем более, что собственные приложения Google ведут себя таким образом.
Пожалуйста, помогите!:) Спасибо.
Решение
Благодаря Jagoan Neon найдено решение. Я закончил тем, что закрыл свой ответ внутри пользовательского RecyclerView
для удобства использования.
public class MultiClickRecyclerView extends RecyclerView {
public MultiClickRecyclerView(Context context) {
super(context);
}
public MultiClickRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MultiClickRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN && this.getScrollState() == RecyclerView.SCROLL_STATE_SETTLING) {
this.stopScroll();
}
return super.onInterceptTouchEvent(event);
}
}
Ответы
Ответ 1
Сначала вы должны определить OnItemTouchListener, возможно, как глобальную переменную, подобную этой:
RecyclerView.OnItemTouchListener mOnItemTouchListener = new RecyclerView.OnItemTouchListener() {
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
if (e.getAction() == MotionEvent.ACTION_DOWN && rv.getScrollState() == RecyclerView.SCROLL_STATE_SETTLING) {
Log.d(TAG, "onInterceptTouchEvent: click performed");
rv.findChildViewUnder(e.getX(), e.getY()).performClick();
return true;
}
return false;
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {}
};
Почему ACTION_DOWN и SCROLL_STATE_SETTLING?
Событие движения ACTION_DOWN будет срабатывать при нажатии на RecyclerView, и в этот точный момент времени RecyclerView изменяет свое состояние прокрутки на SCROLL_STATE_SETTLING, когда он пытается остановить прокрутку. И это точно, когда вы хотите выполнить щелчок.
И не забудьте вернуть true, чтобы вы сообщали своему RecyclerView, что вы использовали MotionEvent. В противном случае ваш метод выполнения() может быть вызван более одного раза.
Добавьте его в свой RecyclerView at onResume и удалите его в onPause, и вы все настроены;)
ОБНОВЛЕНИЕ
Добавьте нулевой checker, когда вы делаете findChildViewUnder - он возвращает null, когда вы не касаетесь определенной ячейки.
View childView = rv.findChildViewUnder(e.getX(), e.getY());
if (childView != null) childView.performClick();
ОБНОВЛЕНИЕ 2
Оказывается, что достаточно остановить прокрутку RecyclerView. Сделайте это вместо:
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
if (e.getAction() == MotionEvent.ACTION_DOWN && rv.getScrollState() == RecyclerView.SCROLL_STATE_SETTLING)
rv.stopScroll();
return false;
}