Подтвердить что ты не робот

Предотвращение использования 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);
   }
}
4b9b3361

Ответ 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;
    }