SwipeRefreshLayout嵌套ViewPager
最近在项目中用到了SwipeRefreshLayout控件,以实现下拉刷新,在我的SwipeRefreshLayout布局中存在一个ViewPager。那么问题就出现了,当我对ViewPager进行左右滑动时,只要你的滑动手势有偏下,即往左下或者右下滑动时,会触发SwipeRefreshLayout的下拉动作,导致不能正常对ViewPager进行滑动操作。
解决方法
对于Android的事件分发机制在这篇文章里就不再赘述,之后有时间再写篇文章针对事件分发进行阐述吧。那对于本文描述的这种情况,其实思路很简单,即对用户的滑动手势进行判断,下拉事件就交给SwipeRefreshLayout处理,左右滑动事件SwipeRefreshLayout就不进行拦截,直接下发到ViewPager进行处理。那怎么区分下拉还是左右滑动呢,我用到的一个思路就是根据用户移动手势的X方向位移dx和Y方向位移dy进行判断,如果dx>dy,那么就认为是左右滑动,交给ViewPager,如果dy>dx,就认为是上下滑动,交给SwipeRefreshLayout并拦截(看源码你会发现SwipeRefreshLayout是继承ViewGroup的,对事件进行拦截后就不会再下发到子View,具体流程在此不赘述)。
代码示例
下面的代码是我重写的一个SwipeRefreshLayout,对用户滑动手势进行了判断处理:
public class SunnySwipeRefreshLayout extends SwipeRefreshLayout {
private float startX;
private float startY;
private boolean mIsXMove;// 是否横向拖拽
private final int mTouchSlop;// getScaledTouchSlop()得来的一个距离,表示滑动的时候,手势移动要大于这个距离才开始移动控件,ViewPager就是用这个距离来判断用户是否翻页
public SunnySwipeRefreshLayout (Context context, AttributeSet attrs) {
super(context, attrs);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
mIsXMove = false;
startX = ev.getX();
startY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
// 如果横向移动则不拦截,直接return false;
if (mIsXMove) {
return false;
}
float endX = ev.getX();
float endY = ev.getY();
float distanceX = Math.abs(endX - startX);
float distanceY = Math.abs(endY - startY);
// 如果dx>xy,则认定为左右滑动,将事件交给viewPager处理,return false
if (distanceX > mTouchSlop && distanceX > distanceY) {
mIsXMove= true;
return false;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mIsXMove= false;
break;
}
// 如果dy>dx,则认定为下拉事件,交给swipeRefreshLayout处理并拦截
return super.onInterceptTouchEvent(ev);
}
}
这么处理之后,在这个下拉刷新布局中再嵌套ViewPager或是其他存在左后滑动操作的控件时,就不会再产生这种滑动冲突啦。
大家如果有更好的方案欢迎留言讨论~