关于ViewPager嵌套内部ViewPager不能响应滑动的的分析与解决

             在实际开发中会遇到很多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;
   }
  
  }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值