Android-SwipeRefreshLayout与ViewP

2020-07-09  本文已影响0人  zzq_nene

内部拦截法

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);
    }
上一篇下一篇

猜你喜欢

热点阅读