10

ПроблемыПредотвращение RecyclerView от употребления события прикосновения

Я пытаюсь моделировать точное поведение Play приложения Google.

В настоящее время, когда вы прокручиваете категорию, например «Лучшие игры», и вы касаетесь списка, эта ячейка, а также 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 Неон решение было найдено. Я закончил тем, что закрыл свой ответ внутри пользовательского 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); 
    } 
} 

ответ

9

Во-первых, вы должны определить 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, когда он пытается остановить прокрутку.И это именно то, когда вы хотите выполнить щелчок.

И помните, что верните истину, чтобы сообщить своему RecyclerView, что вы использовали MotionEvent. В противном случае ваша функция performClick() может вызываться более одного раза.

Добавьте его в RecyclerView в onResume и удалить его OnPause, и вы все сделали;)

UPDATE

Добавить нуль проверки, когда вы делаете findChildViewUnder - это возвращает нуль, когда вам «не касаясь определенной ячейки.

View childView = rv.findChildViewUnder(e.getX(), e.getY()); 
if (childView != null) childView.performClick(); 

UPDATE 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; 
    } 
+0

Я выполнил ваш ответ сегодня утром, и мы почти у цели! Странно; это решение работает примерно в 30% случаев, в зависимости от того, как медленно я перетаскиваю заранее. Я выхожу из состояния движения и состояния прокрутки, и, похоже, нет разницы с тем, когда он работает, а когда нет. Если я прокручу список быстро, а затем коснувшись пальцем, он почти никогда не регистрирует прикосновение и не показывает пульсацию (как это делает приложение Google Play). Он постоянно работает для вас? Я использую макет сетки с двумя интервалами, хотя я бы не подумал, что это изменит ситуацию. – vguzzi

+0

Вы видели мое второе редактирование? –

+0

Да, сейчас я нахожусь в режиме ожидания просмотра прокрутки. Я также попробовал предыдущие предложения. – vguzzi

Смежные вопросы