Внутренний Recyclerview не получает событие клика

У меня есть родительский recyclerview, заполненный картами, и внутренний recyclerview внутри каждой карты.

Прокрутка для внутреннего recyclerview внутри карточек была отключена, но это также повлияло на способность внутреннего recyclerview получать события кликов.

Чтобы отключить прокрутку, я следовал очень похожему подходу к ответу Лукаса Кроуфорда здесь, в котором предлагалось создать собственный класс recyclerview и переопределить dispatchTouchEvent: Отключить прокрутку в дочернем Recyclerview android

Мой класс выглядит так:

public class ScrollThroughRecyclerView extends RecyclerView {

    private boolean isOnClick;

    public ScrollThroughRecyclerView(Context context) {
        super(context);
    }

    public ScrollThroughRecyclerView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ScrollThroughRecyclerView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        final int action = ev.getAction();
        final int actionMasked = action & MotionEvent.ACTION_MASK;
        Log.e("actionmasked", "" + actionMasked);

        switch (actionMasked) {
            case MotionEvent.ACTION_DOWN:
                Log.e("motionDown", "onclick: "+isOnClick);
                isOnClick = true;
                break;
            case MotionEvent.ACTION_MOVE:
                isOnClick = false;
                Log.e("motionMove", "onclick: "+isOnClick);
                return true;
            case MotionEvent.ACTION_UP:
                Log.e("motionUp", "onclick: "+isOnClick);
                if (isOnClick) {
                    return super.dispatchTouchEvent(ev);
                }
                break;
            default:
                return true;
        }
        return true;
    }

Однако он никогда не регистрирует события щелчка, но прокрутка отключена правильно.

Как я могу заставить внутренний recyclerview регистрировать события кликов?


person Simon    schedule 30.08.2015    source источник
comment
Возможно, вам будет лучше с ExpandableListView   -  person Phantômaxx    schedule 30.08.2015


Ответы (1)


Итак, примерно через день, пытаясь понять это, мне наконец удалось решить эту проблему.

После того, как вы сделали все эти шаги, вы должны получить следующее:

  • Внешний recyclerview, которому не мешают события прокрутки внутреннего recyclerview — прокрутка во внутреннем recyclerview отключена. Это полезно в сценарии, когда вы хотите использовать сворачивающуюся панель инструментов как с внешним, так и с внутренним recyclerview.
  • На элементы внутреннего recyclerview можно щелкнуть (какая польза от внутреннего recyclerview, показывающего список элементов, если на них нельзя щелкнуть).

Во-первых, я посмотрел несколько видео на тему тач в андроиде: https://www.youtube.com/watch?v=SYoN-OvdZ3M&list=PLonJJ3BVjZW6CtAMbJz1XD8ELUs1KXaTD&index=19

Я получил ссылку на видео отсюда: Android: разница между onInterceptTouchEvent и dispatchTouchEvent?

Теперь мне пришлось настроить onDispatchTouchEvent:

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    final int action = ev.getAction();
    final int actionMasked = action & MotionEvent.ACTION_MASK;
    Log.e("actionmasked", "" + actionMasked);
    switch (actionMasked) {
        case MotionEvent.ACTION_DOWN:
            return super.dispatchTouchEvent(ev);
        case MotionEvent.ACTION_MOVE:
            return true;
        case MotionEvent.ACTION_CANCEL:
            return true;
        case MotionEvent.ACTION_UP:
            return super.dispatchTouchEvent(ev);
        default:
            return super.dispatchTouchEvent(ev);
    }

Я вызвал super.dispatchTouchEvent(ev) как в DOWN, так и в UP, а также в случаях по умолчанию, поскольку мы хотим, чтобы дочерний элемент внутреннего recyclerview обрабатывал эти события. Событие должно перейти из группы просмотра, которая является этим настраиваемым представлением повторного использования (ScrollThroughRecyclerView), в представления в пределах представления повторного использования.

Для MOVE и CANCEL мы возвращаем true, чтобы сказать, что внутреннее представление повторного использования обработало эти события, и событие может вернуться обратно к родителю, что позволит правильно прокручивать внешнее представление повторного использования. Это не повлияет на поведение приложения при сворачивании панели инструментов.

Теперь нам нужен собственный метод onInterceptTouchEvent:

@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
    final int action = e.getAction();
    final int actionMasked = action & MotionEvent.ACTION_MASK;
    if (actionMasked == MotionEvent.ACTION_DOWN) {
        return false; //intercepted by the viewgroup and passed down to child
    } else if (actionMasked == MotionEvent.ACTION_UP) {
        return false; //intercepted by the viewgroup and passed down to child
    }
    return super.onInterceptTouchEvent(e);
}

И для UP, и для DOWN мы возвращаем false, так как мы хотим, чтобы дочерний элемент внутри внутреннего recyclerview обрабатывал эти события (из UP и DOWN мы можем определить, какой элемент в recyclerview был на самом деле нажат).

Для всего остального мы используем поведение по умолчанию, поэтому я назвал: super.onInterceptTouchEvent(e)

Теперь в адаптере recyclerview:

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
    if (holder instanceof PostViewHolder) {
        ((PostViewHolder) holder).rl.setOnTouchListener(onTouchListener);
    }

Установите сенсорный прослушиватель на представление, в котором вы прослушиваете событие касания в своем повторном просмотре. Для меня, поскольку я прослушиваю клики по всей строке recyclerview, я устанавливаю сенсорный прослушиватель на rl, что означает относительный макет строки, отображаемой в recyclerview.

Touchlistener не будет получать события движения DOWN и UP, через которые группа просмотра прошла в методе onInterceptTouchEvent.

Поскольку у нас есть только события движения DOWN и UP, обнаружение кликов может быть немного более утомительным, чем обычный способ сказать setOnClickListener. Кроме того, поскольку вы используете Touch, Touch фактически переопределяет onClickListener, и onClickListener ничего не вызывает. Для обнаружения кликов по элементу в recyclerview через onTouchListener вам понадобится этот метод:

View.OnTouchListener onTouchListener = new View.OnTouchListener() {
    private float startX;
    private float startY;
    private static final int CLICK_ACTION_THRESHHOLD = 5;
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                startX = event.getX();
                startY = event.getY();
                break;
            case MotionEvent.ACTION_UP: {
                float endX = event.getX();
                float endY = event.getY();
                if (isAClick(startX, endX, startY, endY)) {
                    Log.e("UserClick", "user has clicked");// WE HAVE A CLICK!!
                }
                break;
            }
            default:
                return true;
        }
        return true;
    }

    private boolean isAClick(float startX, float endX, float startY, float endY) {
        float differenceX = Math.abs(startX - endX);
        float differenceY = Math.abs(startY - endY);
        if (differenceX > CLICK_ACTION_THRESHHOLD || differenceY > CLICK_ACTION_THRESHHOLD) {
            return false;
        }
        return true;
    }
};
person Simon    schedule 30.08.2015
comment
Большое спасибо за объяснение, это помогло мне с аналогичной проблемой :) - person Amir Dora.; 29.03.2020