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);
}