美文网首页
Android-SwipeRefreshLayout与ViewP

Android-SwipeRefreshLayout与ViewP

作者: zzq_nene | 来源:发表于2020-07-09 21:04 被阅读0次

    内部拦截法

    1.ViewPager自定义实现

    // ViewPager中的代码
        //内部拦截法-需要父ViewGroup的配合
        @Override
        public boolean dispatchTouchEvent(MotionEvent event) {
            int x = (int) event.getX();
            int y = (int) event.getY();
    
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN: {
                    // 父ViewGroup一定不能拦截ACTION_DOWN事件
                    // 这个方法解决SwipeRefreshLayout嵌套ViewPager滑动冲突问题
                    ViewCompat.setNestedScrollingEnabled(this, true);
                    getParent().requestDisallowInterceptTouchEvent(true);
                    break;
                }
                case MotionEvent.ACTION_MOVE: {
                    int deltaX = x - mLastX;
                    int deltaY = y - mLastY;
                    // 如果是水平滑动时,第一次move事件会进入,然后子View又设置
                    // requestDisallowInterceptTouchEvent(false);
                    // 这样又允许父ViewGroup进行拦截,当第二次move事件进入时
                    // 这样父ViewGroup中的dispatchTouchEvent中的intercepted=true
                    // 在父ViewGroup中的dispatchTouchEvent的末尾对cancelChild定义的时候
                    // final boolean cancelChild = resetCancelNextUpFlag(target.child)|| intercepted;
                    // 这样一来,子View就会进行取消事件的回调。
                    // 因为子View执行了cancel,所以mFirstTouchTarget = next,而next其实就是null
                    // 这里又置空了
                    if (Math.abs(deltaX) > Math.abs(deltaY)) {
                        getParent().requestDisallowInterceptTouchEvent(false);
                    }
                    break;
                }
                case MotionEvent.ACTION_UP: {
                    break;
    
                }
                default:
                    break;
            }
    
            mLastX = x;
            mLastY = y;
            return super.dispatchTouchEvent(event);
        }
    

    2.SwipeRefreshLayout外部放开DOWN事件

    // SwipeRefreshLayout中的代码
        @Override
        public boolean onInterceptTouchEvent(MotionEvent event) {
            //内部拦截法
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                super.onInterceptTouchEvent(event);
                return false;
            }
    
            return true;
        }
    

    滑动实现问题

    其实这样做,是有问题的,ViewPager并不能实现左右滑动的效果。
    但是上面这样的做法,并不能完美实现拦截,ViewPager的左右滑动不能实现。而内部ViewPager的super.dispatchTouchEvent的确是返回了true,那么这里拦截不能实现,不能由ViewPager消化事件,则应该是getParent().requestDisallowInterceptTouchEvent方法没起作用。
    但是因为SwipeRefreshLayout重写了requestDisallowInterceptTouchEvent,

    @Override
    public void requestDisallowInterceptTouchEvent(boolean b) {
        // if this is a List < L or another view that doesn't support nested
        // scrolling, ignore this request so that the vertical scroll event
        // isn't stolen
        if ((android.os.Build.VERSION.SDK_INT < 21 && mTarget instanceof AbsListView)
                || (mTarget != null && !ViewCompat.isNestedScrollingEnabled(mTarget))) {
            // Nope.
        } else {
            super.requestDisallowInterceptTouchEvent(b);
        }
    }
    

    正常情况下,||或条件的前半部分都会满足版本大于21,而我们这里的mTarget是ViewPager,所以并不是AbsListView,而||条件的后半部分,mTarget != null是满足的,而ViewCompat.isNestedScrollingEnabled(mTarget)其实内部实现如下:

    public static boolean isNestedScrollingEnabled(@NonNull View view) {
        if (Build.VERSION.SDK_INT >= 21) {
            return view.isNestedScrollingEnabled();
        }
        if (view instanceof NestedScrollingChild) {
            return ((NestedScrollingChild) view).isNestedScrollingEnabled();
        }
        return false;
    }
    

    从这里看出,其实就是会调用了ViewPager的isNestedScrollingEnabled()方法,其实就是调用了View.isNestedScrollingEnabled()方法,而ViewPager.isNestedScrollingEnabled()其实是返回了false,返回false,导致SwipeRefreshLayout重写了的requestDisallowInterceptTouchEvent是执行了if条件,而不是执行else,这样就什么都没执行
    所以这里事件冲突的解决,其实还需要给ViewPager设置isNestedScrollingEnabled()为true
    即调用:在内部拦截法的getParent().requestDisallowInterceptTouchEvent(true);之前

    ViewCompat.setNestedScrollingEnabled(this, true);
    

    解决方法二

    还有一种方式,就是重写requestDisallowInterceptTouchEvent方法,在SwipeRefreshLayout中。
    即在(mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;的计算结果在move事件中要为true

        @Override
        public void requestDisallowInterceptTouchEvent(boolean b) {
            Class clazz = ViewGroup.class;
    
            try {
                Field mGroupFlagsField =  clazz.getDeclaredField("mGroupFlags");
                mGroupFlagsField.setAccessible(true);
                int c = (int) mGroupFlagsField.get(this);
                Log.e("leo", "dispatchTouchEvent: c " + c);
                if (b) {
                    // 这里的数值,可以采用没有冲突的代码,直接打印来用。
                    // 为true的时候,即mGroupFlags的值需要& FLAG_DISALLOW_INTERCEPT不为0
                    mGroupFlagsField.set(this, 2900051);
                } else {
                    mGroupFlagsField.set(this, 2245715);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
    
    //        super.requestDisallowInterceptTouchEvent(b);
        }
    

    相关文章

      网友评论

          本文标题:Android-SwipeRefreshLayout与ViewP

          本文链接:https://www.haomeiwen.com/subject/lacecktx.html