场景例子:
我使用LazyViewPager 里边嵌套了几个Fragment,其中有一个Fragment中是一个上下滑动的RecycleView,这个RecycleView中是多布局实现的,其中有一个布局是左右滑动的,这个时候我们会发现 , 在这个左右滑动布局滑动到最左边或者最右边的时候ViewPager会响应左右滑动事件,现在的需求是手指滑动这个左右滑动布局的时候即使滑动到左右顶端让它划不动,不能带动ViewPager滑动。
解决:
这个问题想着应该是事件分发方法和Touch方法的关系 ,我们去看一下LazyViewPager的这两个方法:
{
final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
// Always take care of the touch gesture being complete.
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
// Release the drag.
if (DEBUG) Log.v(TAG, "Intercept done!");
mIsBeingDragged = false;
mIsUnableToDrag = false;
mActivePointerId = INVALID_POINTER;
return false;
}
// Nothing more to do here if we have decided whether or not we
// are dragging.
if (action != MotionEvent.ACTION_DOWN) {
if (mIsBeingDragged) {
if (DEBUG) Log.v(TAG, "Intercept returning true!");
return true;
}
if (mIsUnableToDrag) {
if (DEBUG) Log.v(TAG, "Intercept returning false!");
return false;
}
}
switch (action) {
case MotionEvent.ACTION_MOVE: {
final int activePointerId = mActivePointerId;
if (activePointerId == INVALID_POINTER) {
// If we don't have a valid id, the touch down wasn't on content.
break;
}
final int pointerIndex = MotionEventCompat.findPointerIndex(ev, activePointerId);
final float x = MotionEventCompat.getX(ev, pointerIndex);
final float dx = x - mLastMotionX;
final float xDiff = Math.abs(dx);
final float y = MotionEventCompat.getY(ev, pointerIndex);
final float yDiff = Math.abs(y - mLastMotionY);
final int scrollX = getScrollX();
final boolean atEdge = (dx > 0 && scrollX == 0) || (dx < 0 && mAdapter != null &&
scrollX >= (mAdapter.getCount() - 1) * getWidth() - 1);
if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);
if (canScroll(this, false, (int) dx, (int) x, (int) y)) {
// Nested view has scrollable area under this point. Let it be handled there.
mInitialMotionX = mLastMotionX = x;
mLastMotionY = y;
return false;
}
if (xDiff > mTouchSlop && xDiff > yDiff) {
if (DEBUG) Log.v(TAG, "Starting drag!");
mIsBeingDragged = true;
setScrollState(SCROLL_STATE_DRAGGING);
mLastMotionX = x;
setScrollingCacheEnabled(true);
} else {
if (yDiff > mTouchSlop) {
// The finger has moved enough in the vertical
// direction to be counted as a drag... abort
// any attempt to drag horizontally, to work correctly
// with children that have scrolling containers.
if (DEBUG) Log.v(TAG, "Starting unable to drag!");
mIsUnableToDrag = true;
}
}
break;
}
case MotionEvent.ACTION_DOWN: {
**
}
case MotionEventCompat.ACTION_POINTER_UP:
**
}
return mIsBeingDragged;
}
Down 和UP方法我们先不看
在这里我们都知道这个方法的返回值代表这个ViewPager会不会去拦截事件,在返回true的时候回去拦截事件,然后这个事件就交给了ViewPager的Touch事件处理了,所以在这个地方我们在没有滑动到横向布局的两个顶端的时候它
一直返回的是false,但是在顶端的后就返回了true,这个mIsBeingDragged参数我们看一下在什么位置被设置成了true , if (xDiff > mTouchSlop && xDiff > yDiff) 这个判断的时候设置成了true,但是这个判断是正确的,
那我们就想办法在横向滑动到顶端的时候返回false就行了,我们看if (canScroll(this, false, (int) dx, (int) x, (int) y)) 这个判断是返回的false,这个好像符合我们的要求,看判断的方法名canScroll,字面意思就很
明白了,能不能滑动,我们去看一下这个方法,
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())) {
return true;
}
}
}
return checkV && ViewCompat.canScrollHorizontally(v, -dx);
}
看了这个方法,原来是在这里做了处理,这个方法的意思就是说,找到这个父级View然后循环他的子View看看还能不能滑动,返回true的意思就是上个方法中if语句成立,ViewPager不会拦截事件,就交给了横向滑动的布局,
这个时候我们就有思路了,在这个地方我是不是能把我想要处理的那个控件找出来然后见到他就return true 呢,这个时候我们找到我们要屏蔽ViewPager的控件看一下,我找到了这个RecycleView控件,
LinearLayoutManager layout = new LinearLayoutManager(m_Context);
layout.setOrientation(LinearLayoutManager.HORIZONTAL);
holder.rlv_.setLayoutManager(layout);
holder.rlv_.setAdapter(adapter);
这个时候我们需要做一个唯一标识,我们在这里setTag一下,holder.rlv_.setTag("dontTouch");标识一下不能被触碰,然后我们在ViewPager中去找到他,
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(child instanceof RecyclerView && ((RecyclerView)child).getLayoutManager().getLayoutDirection() == HORIZONTAL){
if(child.getTag() != null && child.getTag().equals("dontTouch")){
return true ;
}
}
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())) {
return true;
}
}
}
return checkV && ViewCompat.canScrollHorizontally(v, -dx);
}这个是改过之后的判断方法,这里我首先判断了一下是不是横向滑动的RecycleView,然后如果是我们去判断Tag是不是我们的标记,是那就直接return true 意思就是见到了就直接返回一个让ViewPager不要管,好了这个问
题就算是解决完成了。 在这里可能有小伙伴想要直接用的时候看的不太明白,感觉不是那么刚好是LazyViewPager和RecycleView,就是这样的,这两个只是我在这里用的是 ,但是看明白之后都是这个原理,RecycleView这个
可以用任何的控件代替,因为你只要在方法里边找到他就行了,而LazyViewPager这个,仔细想这个控件和ViewPager是通着的,所以想要这样用,可以借鉴一下LazyViewPager的事件分发方法然后去集成重写一下这个方法就有了。
以上是一个菜鸟的个人见解,有好的看法欢迎评论 。先谢过了~~~~