解决Scrollview嵌套recycleview的显示不完全,滑动冲突问题

  1. 先上一张效果图:

解决Scrollview嵌套recycleview的显示不完全,滑动冲突问题_第1张图片

  1. 解决显示不完全的问题
    这个问题只要重写recycleview的onmeasure方法即可.比如下面的代码,我将recycleview的高度重写测量为500.

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {


        int expandSpec = MeasureSpec.makeMeasureSpec(500,

                MeasureSpec.AT_MOST);

        super.onMeasure(widthMeasureSpec, expandSpec);
    }

3 .解决滑动冲突问题
按照我上面的代码解决了显示不完全的问题,这时当你滑动recycleview时,无论如何会先响应recycleview的滑动事件,然后才响应scrollview的滑动事件,这样就达不到图片上的先将scrollview滑动指定位置,然后才让recycleview滑动的效果.原因就是recycleview抢走了scrollview的滑动事件,让recycleview消费了,scrollview就无法滑动了.
通过调用recyclerView.setNestedScrollingEnabled(false);就可以让recycleview不抢走scrollview的事件,这时在recycleview上滑动,也只会响应scrollview的滑动事件,recycleview是无法滑动的.这样还是达不到要求.于是就只能重写scrollview的onTouchevent方法和recycleview的dispatchtouchevent方法来解决冲突问题.


    @Override
    public boolean onTouchEvent(MotionEvent ev) {
       boolean isup  = false;
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downY = (int) ev.getY();
                isup = super.onTouchEvent(ev);
                break;
            case MotionEvent.ACTION_MOVE:
                //防止从intercept直接跳过来
                if (downY == 0) {
                    downY = (int) ev.getY();
                }
                //获取偏移量
                int diffy = (int) (ev.getY() - downY);
                if (diffy < 0) {//上拉
                    if (getScrollY() >= measuredHeight) {//当scrollview滑动的距离大于指定的距离时就让recycleview滑动
                       //修改父类的上一次事件的位置,防止跳动
                       mLastMotionY = (int) ev.getY();
                        //置顶了实际上是要让recycleview滑动了
                        isup = recyclerView.dispatchTouchEvent(ev);
                    } else {
                        //scrollview滑动
                        isup = super.onTouchEvent(ev);

                    }
                } else if (diffy > 0) { //下拉
                    //如果recycleview没置顶就应该先让recycleview滑动
                    if (!isTop) {
                        //修改父类的上一次事件的位置
                        mLastMotionY = (int) ev.getY();
                        //recycleview滑动
                        isup = recyclerView.dispatchTouchEvent(ev);

                    } else {
                        //scrollview滑动
                        isup = super.onTouchEvent(ev);
                    }

                }
                //记录上一次的位置
                downY = (int) ev.getY();
                break;
            case MotionEvent.ACTION_UP:
                downY = 0;
               isup = super.onTouchEvent(ev);
                break;

        }

        return isup;
    }

上面的代码就是我重写的scrollview的onTouchevent方法,注释的很详细了,我这里只重点说明一个问题,mLastMotionY 是父类Scrollview的私有变量,如果你是继承系统的Scrollview是修改不了的,我这里是将系统的Scrollview拷贝一份出来,然后将mLastMotionY的权限修改为公有的就可以修改了.这里为什么要修改这个属性呢,因为我调用recyclerView.dispatchTouchEvent(ev);让recycleview滑动,并没有调用super.onTouchevent方法这样父类Scrollview的mLastMotionY属性就不会被修改,需要我们主动修改.如果不修改这个属性,当我们先往上滑动在下拉,滑动完recycleview,让scrollview滑动时会突然跳很大一段距离.

下面是重写recycleview的dispatchtouchevent方法

  @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {

        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downY = (int) ev.getY();
                //在down的时候拦截,否则后面都拦截不到
                getParent().requestDisallowInterceptTouchEvent(true);
                break;
            case MotionEvent.ACTION_MOVE:
                LinearLayoutManager layoutManager = (LinearLayoutManager) this.getLayoutManager();
                int firstCompletelyVisibleItemPosition = layoutManager.findFirstCompletelyVisibleItemPosition();
                if (scrollYScrollView != null) {
                    if (firstCompletelyVisibleItemPosition == 0) {//recycleview置顶,通知scrollview
                        scrollYScrollView.setTop(true);
                    } else {
                        scrollYScrollView.setTop(false);
                    }
                }
                int diffy = (int) (ev.getY() - downY);
                if (diffy > 0) {//下拉
                    if (firstCompletelyVisibleItemPosition == 0) {//当recycleview滑倒顶部时就不拦截,让scrollview滑动
                        getParent().requestDisallowInterceptTouchEvent(false);
                    } else {
                        //recycleview没滑到顶部,就先让recycleview滑动
                        getParent().requestDisallowInterceptTouchEvent(true);
                    }
                } else if (diffy < 0) {//上拉

                    if (isTop) {//当scrollview置顶时就拦截,让recycleview滑动
                        getParent().requestDisallowInterceptTouchEvent(true);


                        }
                    } else {
                        //这里不仅要拦截,还要返回false,直接让scrollview滑动,不让recycleview滑动,
                        getParent().requestDisallowInterceptTouchEvent(false);
                        return false;

                    }
                }
                //记录上一次的位置
                downY = (int) ev.getY();
                break;

        }

        return super.dispatchTouchEvent(ev);
    }

要看懂我重写的这个方法,就应该对android的事件分发机制有所了解,并且知道getParent().requestDisallowInterceptTouchEvent(false)方法的用处,这里如果传入是false表示父类可以拦截事件,当传入true时表示父类不可以拦截事件.还有我这里的isTop表示Scrollview是否滑动到指定的位置,不需要滑动了.这个是外面传入进来的.

你可能感兴趣的:(android开发)