Inner Recyclerview not receiving click event

三世轮回 提交于 2020-01-01 17:26:30

问题


I have a parent recyclerview with filled with cards and an inner recyclerview inside each card.

The scrolling for the inner recyclerview inside the cards has been disabled but this has also affected the inner recyclerview's ability to receive click events.

To disable the scrolling, I had followed a very similar approached to Lucas Crawford's answer here which suggested that you create a custom recyclerview class and override dispatchTouchEvent: Disable Scrolling in child Recyclerview android

My class looks like this:

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;
    }

However, it never registers the click events but the scrolling is disabled correctly.

How can I get the inner recyclerview to register the click events?


回答1:


So after about a day trying to figure this out, I finally managed to solve this problem.

After you have done all these steps, you should end up with:

  • An outer recyclerview which is not hindered by the scroll events of an inner recyclerview - scrolling in the inner recyclerview is disabled. This is useful in the scenario when you want to use the collapsing toolbar approach with both the outer and inner recyclerview.
  • The elements in the inner recyclerview can be clicked on (what use is an inner recyclerview showing a list of items if they cannot be clicked upon).

Firstly, I watched a few videos on the topic of touch in android: https://www.youtube.com/watch?v=SYoN-OvdZ3M&list=PLonJJ3BVjZW6CtAMbJz1XD8ELUs1KXaTD&index=19

I got the video link from here: Android: Difference between onInterceptTouchEvent and dispatchTouchEvent?

Now, I had to customise my 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);
    }

I have called super.dispatchTouchEvent(ev) in both DOWN and UP and the default cases as we want the child of the inner recyclerview to handle those events. The event must go from the viewgroup which is this custom recyclerview (ScrollThroughRecyclerView) to the views within the recyclerview.

For MOVE and CANCEL, we return true to say that the inner recyclerview has handled those events and the event can return back to the parent which will allow the outer recyclerview to scroll properly. This will not hinder the app behavior of the collapsing toolbar.

Now we need a custom onInterceptTouchEvent method:

@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);
}

For both UP and DOWN, we return false as we want the child inside the inner recyclerview to handle those events (from UP and DOWN, we can determine which item in the recyclerview was actually clicked on).

For everything else, we use the default behavior so I have called: super.onInterceptTouchEvent(e)

Now in the recyclerview adapter:

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

Set a touchlistener onto the view you are listening for touch event on in your recyclerview. For me, since I'm listening to clicks on the entire line of the recyclerview, I set the touch listener on rl, which stands for relativeLayout of the line that are displayed in the recyclerview.

The touchlistener will not receive the DOWN and UP motionevent that the viewgroup passed through in onInterceptTouchEvent method.

Since we have only got a DOWN and an UP motionevent, to detect clicks can be a bit more tedious than the general way of saying setOnClickListener. Furthermore, since you are using Touch, the Touch actually overrides the onClickListener and nothing is called on onClickListener. For detecting the clicks on the item in the recyclerview through onTouchListener, you will need this method:

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;
    }
};


来源:https://stackoverflow.com/questions/32295840/inner-recyclerview-not-receiving-click-event

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!