在实际开发中会遇到很多ViewPager嵌套的问题,比如在ViewPager需要显示轮播图,即是嵌套的问题,在使用过程中会发现,在低版本手机上运行,滑动内部时,外部响应了事件,而这不是我们想要的,那么,造成这个问题的原因是什么呢?其实,凭猜测也应该知道,一定是ViewPager在事件传递的过程中出现了问题,那我们就通过查看源码找下答案:
在ViewPager的onInterceptTouchEvent(MotionEvent ev)方法中,如果它拦截了事件的向下传递,内部ViewPager就不会做出响应,我们看下代码:
这个是当Action_move事件的时候代码,可以看到返回的是true还是falsecanScoll()这个方法是参与决定的,那我们再次点进去,发现什么
它的返回值又是由ViewComPat.canScollHorizontally()这个方法参与决定的,再次点进去;
在点进IMPL,
追溯到最后,我们发现,Action_move那里的返回值是sdk版本参与决定的,这就是为什么会出现内部不能滑动的情况,因为sdk的版本会参与决定拦截返回的是true还是false。
既然找到了问题的原因那我们怎么去解决它呢。这里,给大家提供两种方法:
第一种:从外部ViewPager出发,修改ViewPager的onInterceptTouchEvent(MotionEvent ev)默认返回,即覆写ViewPager,在一定逻辑条件下需要内部响应,返回值设置为false,否则拦截,由外部处理;
public class ParentViewPager extends ViewPager{
private int childVPHeight=0;
public ParentViewPager(Context context) {
super(context);
// TODO Auto-generated constructor stub
init(context);
}
public ParentViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
init(context);
}
private void init(Context context) {
// TODO Auto-generated method stub
// 获取屏幕宽高
WindowManager windowManager = (WindowManager)
context.getSystemService(context.WINDOW_SERVICE);
int disWidth = windowManager.getDefaultDisplay().getWidth();
//根据屏幕的密度来过去dp值相应的px值
childVPHeight=(int) (context.getResources().getDisplayMetrics().density
* disWidth + 0.5f);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent arg0) {
// TODO Auto-generated method stub
//触摸在子ViewPager所在的页面和子ViewPager控件高度之内时
//返回false,此时将会将触摸的动作传给子ViewPager
if(getCurrentItem()==1 && arg0.getY()
return false;
}
return super.onInterceptTouchEvent(arg0);
}
}
第二种:从内部ViewPager出发,同样是覆写ViewPageronInterceptTouchEvent(MotionEvent ev),在一定逻辑条件下需要响应事件,在Action_move事件处请求父View不拦截事件
getParent().requestDisallowInterceptTouchEvent(true)<span style="font-size:18px; font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">交由内部处理。</span>
贴段示例代码
public class ChildViewPager extends ViewPager{
/** 触摸时按下的点 **/
PointF downP = new PointF();
/** 触摸时当前的点 **/
PointF curP = new PointF();
OnSingleTouchListener onSingleTouchListener;
public ChildViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
public ChildViewPager(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
@Override
public boolean onInterceptTouchEvent(MotionEvent arg0) {
// TODO Auto-generated method stub
//当拦截触摸事件到达此位置的时候,返回true,
//说明将onTouch拦截在此控件,进而执行此控件的onTouchEvent
return true;
}
@Override
public boolean onTouchEvent(MotionEvent arg0) {
// TODO Auto-generated method stub
//每次进行onTouch事件都记录当前的按下的坐标
curP.x = arg0.getX();
curP.y = arg0.getY();
if(arg0.getAction() == MotionEvent.ACTION_DOWN){
//记录按下时候的坐标
//切记不可用 downP = curP ,这样在改变curP的时候,downP也会改变
downP.x = arg0.getX();
downP.y = arg0.getY();
//此句代码是为了通知他的父ViewPager现在进行的是本控件的操作,不要对我的操作进行干扰
getParent().requestDisallowInterceptTouchEvent(true);
}
if(arg0.getAction() == MotionEvent.ACTION_MOVE){
//此句代码是为了通知他的父ViewPager现在进行的是本控件的操作,不要对我的操作进行干扰
getParent().requestDisallowInterceptTouchEvent(true);
}
if(arg0.getAction() == MotionEvent.ACTION_UP){
//在up时判断是否按下和松手的坐标为一个点
//如果是一个点,将执行点击事件,这是我自己写的点击事件,而不是onclick
if(downP.x==curP.x && downP.y==curP.y){
onSingleTouch();
return true;
}
}
return super.onTouchEvent(arg0);
}
/**
* 单击
*/
public void onSingleTouch() {
if (onSingleTouchListener!= null) {
onSingleTouchListener.onSingleTouch();
}
}
/**
* 创建点击事件接口
* @author wanpg
*
*/
public interface OnSingleTouchListener {
public void onSingleTouch();
}
public void setOnSingleTouchListener(OnSingleTouchListener
onSingleTouchListener) {
this.onSingleTouchListener = onSingleTouchListener;
}
}