内部Recyclerview没有收到点击事件 [英] Inner Recyclerview not receiving click event

查看:93
本文介绍了内部Recyclerview没有收到点击事件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一张充斥着卡片的父级recyclerview,每张卡内都有一个内部recyclerview.

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

禁用了卡片内部的recyclerview的滚动,但这也影响了内部的recyclerview接收点击事件的能力.

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.

要禁用滚动,我遵循了与Lucas Crawford的答案非常相似的方法,该方法建议您创建一个自定义的recyclerview类并覆盖dispatchTouchEvent:

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

我的课看起来像这样:

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.

如何获取内部recyclerview来注册单击事件?

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

推荐答案

因此,经过大约一天的努力,我终于设法解决了这个问题.

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

完成所有这些步骤之后,您应该会得到以下结果:

  • 不受内部recyclerview的滚动事件影响的外部recyclerview-禁止在内部recyclerview中滚动.如果您想同时使用折叠式工具栏方法和外部和内部的recyclerview,这在场景中很有用.
  • 可以单击内部recyclerview中的元素(内部recyclerview的用途是显示无法单击的项目列表).

首先,我在android上观看了一些有关触摸主题的视频: https://www.youtube.com/watch?v=SYoN-OvdZ3M&list=PLonJJ3BVjZW6CtAMbJz1XD8ELUs1KXaTD&index=19

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

我从这里获得了视频链接: Android:onInterceptTouchEvent和dispatchTouchEvent之间的区别?

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

现在,我必须自定义我的onDispatchTouchEvent:

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

我在DOWN和UP以及默认情况下都调用了super.dispatchTouchEvent(ev),因为我们希望内部recyclerview的子级来处理这些事件.该事件必须从该自定义recyclerview(ScrollThroughRecyclerView)的视图组到recyclerview中的视图.

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.

对于MOVE和CANCEL,我们返回true表示内部recyclerview处理了这些事件,并且该事件可以返回到父事件,这将允许外部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.

现在,我们需要一个自定义的onInterceptTouchEvent方法:

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

对于UP和DOWN,我们都返回false,因为我们希望内部recyclerview中的孩子处理这些事件(从UP和DOWN中,我们可以确定recyclerview中的哪个项目实际上被单击了.)

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).

对于其他所有情况,我们都使用默认行为,因此我将其称为:super.onInterceptTouchEvent(e)

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

现在在recyclerview适配器中:

Now in the recyclerview adapter:

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

在您的recyclerview中,在正在监听触摸事件的视图上设置一个触摸侦听器.对我来说,由于我正在听recyclerview整行的点击,因此将触摸侦听器设置为rl,它表示在recyclerview中显示的行的relativeLayout.

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.

触摸侦听器将不会收到视图组在onInterceptTouchEvent方法中通过的DOWN和UP运动事件.

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

由于我们只有DOWN和UP运动事件,因此检测点击可能比说setOnClickListener的一般方式更加乏味.此外,由于您使用的是Touch,因此Touch实际上会覆盖onClickListener,并且onClickListener上不会调用任何内容.为了通过onTouchListener在recyclerview中检测项目的点击,您将需要以下方法:

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

这篇关于内部Recyclerview没有收到点击事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆