RecyclerView嵌套RecyclerView或ScrollView

RecycrollView嵌套RecyclerView!

 大家可能或多或少都遇到过这种需求,也有很多种情况需要处理多个RecyclerView(或ListView)共同使用。这时就需要用到ScrollView来嵌套RecyclerView,其实也很简单,需要重写一下RecyclerView的LayoutManager,将所有条目的高度计算出来就好了,但是这种处理方式会影响RecyclerView的回收机制,所以不建议大面积使用。关于该解决方案的代码,网上一搜一大把,我就不浪费时间了。 

这里我们主要说一下RecyclerView嵌套ScrollView或者NestedScrollView的场景和滑动冲突处理吧。

RecyclerView嵌套ScrollView呢?

 RecyclerView嵌套ScrollView或NestedScrollView,可能有很多人没有听过这种需求,因为我在网上搜了很多关于嵌套的内容,都是ScrollView嵌套RecyclerView,根本没有RecyclerView嵌套ScrollView的。真尴尬,这里我就整理一下我的解决方法。

有这样一个需求,首先是一个RecyclerView列表,Grid模式,卡片显示样式,在卡片内部需要显示一个列表数据,如果数据超出卡片显示范围需要滑动,这里就需要用到一个RecyclerView 或者 用ScrollView包裹现行布局让他滚动。我的实现思路就是使用ScrollView+LinearLayout的方式实现滚动,这样就会出现两个问题。

第一个问题,就是卡片内部数据无法滑动,这个问题就是我们今天主角。其实解决起来很简单,先看代码:

    /**
     * 禁止父容器及上层容器拦截事件
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                super.requestDisallowInterceptTouchEvent(true);
                break;
            case MotionEvent.ACTION_UP:
                super.requestDisallowInterceptTouchEvent(true);
                break;
        }
        return super.dispatchTouchEvent(ev);
    }

这就解决问题了,原理呢,就是在View树中,ScrollView的父容器以及上层容器在执行onIntercepTouchEvent的时候将事件拦截处理了。这是RecyclerView中的处理:

    @Override
    public boolean onInterceptTouchEvent(MotionEvent e) {
        ...

        switch (action) {
            case MotionEvent.ACTION_DOWN:
                ...
                if (mScrollState == SCROLL_STATE_SETTLING) {
                    getParent().requestDisallowInterceptTouchEvent(true);
                    setScrollState(SCROLL_STATE_DRAGGING);
                }
                ...
                startNestedScroll(nestedScrollAxis, TYPE_TOUCH);
                break;
            ...
            case MotionEvent.ACTION_MOVE: {
                ...
                if (mScrollState != SCROLL_STATE_DRAGGING) {
                    final int dx = x - mInitialTouchX;
                    final int dy = y - mInitialTouchY;
                    boolean startScroll = false;
                    if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
                        mLastTouchX = x;
                        startScroll = true;
                    }
                    if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
                        mLastTouchY = y;
                        startScroll = true;
                    }
                    if (startScroll) {
                        setScrollState(SCROLL_STATE_DRAGGING);
                    }
                }
            } break;
            ...
        }
        return mScrollState == SCROLL_STATE_DRAGGING;
    }

 这样就导致了在滑动时内层的ScrollView无法触发滑动。在看看刚才的解决方案,在事件分发的时候就先禁止其父容器和上层容器拦截事件,这样外层的RecyclerView就无法拦截滑动事件,内层的ScrollView或者NestedScrollView就可以自由滑动了。

    /**
     * 禁止父容器及上层容器拦截事件
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                //这里不要调用自己的requestDisallowInterceptTouchEvent
                //一定要调用父类的 具体原因大家可以探究。
                super.requestDisallowInterceptTouchEvent(true);
                break;
            case MotionEvent.ACTION_UP:
                super.requestDisallowInterceptTouchEvent(true);
                break;
        }
        return super.dispatchTouchEvent(ev);
    }

 说完第一个问题了,该说第二个问题了,那就是ScrollView的无法点击问题了。。这个问题网上有很多解决方案,这里我就不一一介绍优缺点了。只介绍一种方案:

    @Override
    public boolean canScrollVertically(int direction) {
        return true;
    }

 并且给ScrollView的直接子布局设置一个点击事件,来响应Item的点击事件。

 RecyclerView嵌套RecyclerView呢? 

关于RecyclerView嵌套RecyclerView 的就比较多了,外层纵向,内层横向,只需要给内层RecyclerView设置一个高度就可以了,这是因为RecyclerView在onLayout的时候处理的问题。

还有就是多个RecyclerView同向嵌套的问题,这种需求目前应该有吧,其实也很简单两种方案,第一种就是让内部Recyclerview可以滚动,给定一个固定高度,让他滚动,解决方案就是上面的方案。第二种就是内部的RecyclerView完全展开,不过这种方案我还是不建议大家用,因为复用不了,用RecyclerView还有什么意义呢。还不如ScrollView呢。
关于RecyclerView的嵌套问题,就先写到这,欢迎大神们留言,讨论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值