Moosphan/Android-Daily-Interview

2020-01-16:为什么ViewPager嵌套ViewPager,内部的ViewPager滚动没有被拦截?

MoJieBlog opened this issue · 3 comments

2020-01-16:为什么ViewPager嵌套ViewPager,内部的ViewPager滚动没有被拦截?
hfr92 commented

被外部的ViewPager拦截了,需要做滑动冲突处理。重写子View的 dispatchTouchEvent方法,在子View需要拦截的时候进行拦截,否则交给父View处理。
image

@Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
      //省略若干代码
        // Nothing more to do here if we have decided whether or not we
        // are dragging.
        //1、move再次进来时mIsUnableToDrag为true
        if (action != MotionEvent.ACTION_DOWN) {
            if (mIsBeingDragged) {
                if (DEBUG) Log.v(TAG, "Intercept returning true!");
                return true;
            }
            //2、由于mIsUnableToDrag为true所以不拦截,move事件正常传递给子ViewPager
            if (mIsUnableToDrag) {
                if (DEBUG) Log.v(TAG, "Intercept returning false!");
                return false;
            }
        }

        switch (action) {
            case MotionEvent.ACTION_MOVE: {
                //省略若干代码,关键点在于canScroll方法,此处会寻找可滑动的子View
                if (dx != 0 && !isGutterDrag(mLastMotionX, dx)
                        && canScroll(this, false, (int) dx, (int) x, (int) y)) {
                    // Nested view has scrollable area under this point. Let it be handled there.
                    mLastMotionX = x;
                    mLastMotionY = y;
                    mIsUnableToDrag = true;//第一次move事件进来时,设置此值为true,下一次move事件进来时,onInterceptTouchEvent直接返回false,看上面1和2
                    return false;
                }
               //省略若干代码
                break;
            }
              //省略若干代码
        }

       //省略若干代码
        return mIsBeingDragged;
    }
//此方法递归寻找可滑动的子View,找到后返回true
protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
        if (v instanceof ViewGroup) {
            final ViewGroup group = (ViewGroup) v;
            final int scrollX = v.getScrollX();
            final int scrollY = v.getScrollY();
            final int count = group.getChildCount();
            // Count backwards - let topmost views consume scroll distance first.
            for (int i = count - 1; i >= 0; i--) {
                // TODO: Add versioned support here for transformed views.
                // This will not work for transformed views in Honeycomb+
                final View child = group.getChildAt(i);
                if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight()
                        && y + scrollY >= child.getTop() && y + scrollY < child.getBottom()
                        && canScroll(child, true, dx, x + scrollX - child.getLeft(),
                                y + scrollY - child.getTop())) {
                     //当child为ViewPager时,返回true
                    return true;
                }
            }
        }

        return checkV && v.canScrollHorizontally(-dx);
    }

答案:ViewPager重写了onInterceptTouchEvent方法,同时在move事件里边递归寻找可滑动的子View(ViewPager),,当它找到可滑动的子View(ViewPager)时,onInterceptTouchEvent返回false,也就是不拦截事件,所以子ViewPager可以滑动