在Android开发过程中,你一定会用到ViewPager这个控件,最让人头疼的就是各种滑动冲突,比如说:在ListView,SrollView中嵌套ViewPager,在作侧边栏滑动时和ViewPager的冲突,甚至还有ViewPager嵌套ViewPager的情况等等,解决起来很麻烦,今天和大家分享一下我心得。这些冲突无非就是横向滑动和纵向滑动的一个冲,而我们要解决的就是要判断将事件给父控件还是子控件处理。
先看代码:
import android.content.Context; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; /** * @Package com.ml.news.ui.view * @ClassName: ChildViewPager * @Description: 解决viewpager内嵌viewpager滑动冲突问题 * @author malong * @date Mar 26, 2015 8:56:24 PM */ public class ChildViewPager extends ViewPager { public ChildViewPager(Context context, AttributeSet attrs) { super(context, attrs); } public ChildViewPager(Context context) { super(context); } // 滑动距离及坐标 归还父控件焦点 private float xDistance, yDistance, xLast, yLast,mLeft; @Override public boolean dispatchTouchEvent(MotionEvent ev) { getParent().requestDisallowInterceptTouchEvent(true); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: Log.d("touch", "ACTION_DOWN"); xDistance = yDistance = 0f; xLast = ev.getX(); yLast = ev.getY(); mLeft = ev.getX(); break; case MotionEvent.ACTION_MOVE: final float curX = ev.getX(); final float curY = ev.getY(); xDistance += Math.abs(curX - xLast); yDistance += Math.abs(curY - yLast); xLast = curX; yLast = curY; if (mLeft<100||xDistance < yDistance) { getParent().requestDisallowInterceptTouchEvent(false); } else { getParent().requestDisallowInterceptTouchEvent(true); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: break; } return super.dispatchTouchEvent(ev); } }核心代码就是 getParent().requestDisallowInterceptTouchEvent(false);这句;当为false则通知父view可以拦截touch事件,由父view处理,而当为true时,则会 阻止父层的View截获touch事件,这样就会返回个子view处理。那么好,解决了这个关键性问题接下来的问题,就是要解决什么时候交给子view或是父view的问题了。当ScrollView和ListView中嵌套ViewPager的时候,多数是在作轮播的幻灯片,冲突无非就是能左右滑动,不能上下滑动的问题;而ViewPager内嵌套ViewPager这是子view不能滑动,一划就是父ViewPager滑动的问题;这两个问题上面的自定义ViewPager都能解决,解决思路是在事件分发down的时候记录xLast和yLast,然后在move的时候比较xDistance和yDistance即x轴差和y轴差,如果x轴差小于y轴差,则说明是上下滑动,此时将事件还给父view,反之左右滑动把事件交给子view,Ok问题解决了。
细心的朋友可能会问,那mLeft是干嘛用的阿?是这样的,可能我在项目中会用道侧边栏,我的用的是SlidingMenuLibrary这个开源组件,可以设置将侧边栏划出的区域,这时就会跟ViewPager冲突,都是左右滑,事件被子view吃掉了,侧边栏画不出来,所以我就加个mLeft当点击屏幕左侧边缘(即down时x<100)时,将事件还给父view这样既不影响侧边栏划出,也不影响ViewPager的滑动。同样你可以举一反三,灵活控制事件响应区域。
再给大家说一个扩展的,如果用过ViewPagerIndicatorLibrary朋友,可能会遇到,ViewPager内套ViewPager,这时我们就想能不能在内部ViewPager滑到最后一页时,父ViewPager在切换到下一页,提供一个思路:就是在事件分发时判断当前页是不是第一页或最后一页,如果是最后一页且向左滑将事件交给父view即可,以此类推。
增强版如下:万能解决方案
import android.content.Context; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; /** * @Package com.ml.news.ui.view * @ClassName: ChildViewPager * @Description: 解决viewpager内嵌viewpager滑动冲突问题 * @author malong * @date Mar 26, 2015 8:56:24 PM */ public class ChildViewPager extends ViewPager { public ChildViewPager(Context context, AttributeSet attrs) { super(context, attrs); } public ChildViewPager(Context context) { super(context); } // 滑动距离及坐标 归还父控件焦点 private float xDistance, yDistance, xLast, yLast,xDown, mLeft; @Override public boolean dispatchTouchEvent(MotionEvent ev) { getParent().requestDisallowInterceptTouchEvent(true); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: Log.d("touch", "ACTION_DOWN"); xDistance = yDistance = 0f; xLast = ev.getX(); yLast = ev.getY(); xDown = ev.getX(); mLeft = ev.getX();// 解决与侧边栏滑动冲突 break; case MotionEvent.ACTION_MOVE: final float curX = ev.getX(); final float curY = ev.getY(); xDistance += Math.abs(curX - xLast); yDistance += Math.abs(curY - yLast); xLast = curX; yLast = curY; if (mLeft < 100 || xDistance < yDistance) { getParent().requestDisallowInterceptTouchEvent(false); } else { if (getCurrentItem() == 0) { if (curX < xDown) { getParent().requestDisallowInterceptTouchEvent(true); } else { getParent().requestDisallowInterceptTouchEvent(false); } } else if (getCurrentItem() == (getAdapter().getCount()-1)) { if (curX > xDown) { getParent().requestDisallowInterceptTouchEvent(true); } else { getParent().requestDisallowInterceptTouchEvent(false); } } else { getParent().requestDisallowInterceptTouchEvent(true); } } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: break; } return super.dispatchTouchEvent(ev); } }