Android中ViewPager带来的滑动卡顿问题解决要点解析
问题说明:
当SwipeRefreshLayout中放置了ViewPager控件,两者的滑动会相互冲突.具体表现为ViewPager的左右滑动不顺畅,容易被SwipeRefreshLayout拦截(即出现刷新的View).
问题原因:
ViewPager本身是处理了滚动事件的冲突,它在横向滑动时会调用requestDisallowInterceptTouchEvent()方法使父控件不拦截当前的Touch事件序列.但是SwipeRefreshLayout的requestDisallowInterceptTouchEvent()方法什么也没有做,所以仍然会拦截当前的Touch事件序列.
问题分析:
为什么SwipeRefreshLayout的requestDisallowInterceptTouchEvent()方法什么都不做?
首先SwipeRefreshLayout继承自ViewGroup.
在requestDisallowInterceptTouchEvent()方法什么都不做的情况下,用户可以从底部下拉刷新一次拉出LoadingView.
如果方法调用ViewGroup的requestDisallowInterceptTouchEvent()方法,可以解决ViewPager的兼容问题,但是用户在界面底部下拉至头部后,无法继续下拉,需要手指放开一次才能拉出LoadingView.
目标分析:
那么为了更加顺滑地滚动,想要的效果当然是一次性拉出LoadingView.既然ViewPager在左右滑动时才会调用requestDisallowInterceptTouchEvent()方法,那么SwipeRefreshLayout只应该在上下滑动时才拦截Touch事件.
具体逻辑如下:
记录是否调用了requestDisallowInterceptTouchEvent()方法,并且设置为true.
在SwipeRefreshLayout中判断是否是上下滑动.
如果同时满足1,2,则调用super.requestDisallowInterceptTouchEvent(true).
否则调用super.requestDisallowInterceptTouchEvent(false).
注意:因为ViewGroup的requestDisallowInterceptTouchEvent方法设置true后,Touch事件在dispatchTouchEvent()方法中就会被拦截,所以需要在dispatchTouchEvent()方法中判断是否为上下滑动.
实现代码(部分):
//非法按键 privatestaticfinalintINVALID_POINTER=-1; //dispatch方法记录第一次按下的x privatefloatmInitialDisPatchDownX; //dispatch方法记录第一次按下的y privatefloatmInitialDisPatchDownY; //dispatch方法记录的手指 privateintmActiveDispatchPointerId=INVALID_POINTER; //是否请求拦截 privatebooleanhasRequestDisallowIntercept=false; @Override publicvoidrequestDisallowInterceptTouchEvent(booleanb){ hasRequestDisallowIntercept=b; //Nope. } @Override publicbooleandispatchTouchEvent(MotionEventev){ switch(ev.getAction()){ caseMotionEvent.ACTION_DOWN: mActiveDispatchPointerId=MotionEventCompat.getPointerId(ev,0); finalfloatinitialDownX=getMotionEventX(ev,mActiveDispatchPointerId); if(initialDownX!=INVALID_POINTER){ mInitialDisPatchDownX=initialDownX; } finalfloatinitialDownY=getMotionEventY(ev,mActiveDispatchPointerId); if(mInitialDisPatchDownY!=INVALID_POINTER){ mInitialDisPatchDownY=initialDownY; } break; caseMotionEvent.ACTION_MOVE: if(hasRequestDisallowIntercept){ //解决viewPager滑动冲突问题 finalfloatx=getMotionEventX(ev,mActiveDispatchPointerId); finalfloaty=getMotionEventY(ev,mActiveDispatchPointerId); if(mInitialDisPatchDownX!=INVALID_POINTER&&x!=INVALID_POINTER&& mInitialDisPatchDownY!=INVALID_POINTER&&y!=INVALID_POINTER){ finalfloatxDiff=Math.abs(x-mInitialDisPatchDownX); finalfloatyDiff=Math.abs(y-mInitialDisPatchDownY); if(xDiff>mTouchSlop&&xDiff*0.7f>yDiff){ //横向滚动不需要拦截 super.requestDisallowInterceptTouchEvent(true); }else{ super.requestDisallowInterceptTouchEvent(false); } }else{ super.requestDisallowInterceptTouchEvent(false); } } break; caseMotionEvent.ACTION_UP: caseMotionEvent.ACTION_CANCEL: if(ev.getAction()==MotionEvent.ACTION_UP||ev.getAction()==MotionEvent.ACTION_CANCEL){ hasRequestDisallowIntercept=false; } break; } returnsuper.dispatchTouchEvent(ev); } privatefloatgetMotionEventY(MotionEventev,intactivePointerId){ finalintindex=MotionEventCompat.findPointerIndex(ev,activePointerId); if(index<0){ return-1; } returnMotionEventCompat.getY(ev,index); } privatefloatgetMotionEventX(MotionEventev,intactivePointerId){ finalintindex=MotionEventCompat.findPointerIndex(ev,activePointerId); if(index<0){ return-1; } returnMotionEventCompat.getX(ev,index); }