SwipeRefreshLayout和ConvenientBanner广告轮播事件冲突解决
查看布局,分析一下布局,这个布局是很常规的首页布局,一个SwipeRefreshLayout下拉刷新包裹一个带head的listview。其中head头部有个广告轮播的控件,可以左右滑动,下拉刷新是上下滑动。
如果直接使用,你会发现,你左右滑动的时候,很不灵敏,有时候会触动下拉刷新。
可以发现淘宝那些广告条,左右滑动非常顺畅,上下滑动下拉刷新也不影响广告轮播。这就是这个博客要解决的一个问题,解决广告条左右滑动和下拉刷新的冲突。
我们理想的处理就是,我们只想直接下拉(x方向没有发生很大的变化)就是下拉刷新,其他小动作大动作的左右滑动,都属于广告轮播。
这样我们先确定处理事件处理分成2个情况。
A,笔直滑动,x坐标左右变化不大。(下拉刷新)
B,左右滑动,斜滑动(广告条)
目标确定了,明确了分2中情况。我们就先从下拉刷新源码入手,因为这样思路比较明确,比较事件传递,先从父类传到子类。那个过程就不再详说了,不明白的可以看一下其他优秀的博客,这篇不错:http://blog.csdn.net/hyp712/article/details/8777835
查看SwipeRefreshLayout源码,其中onInterceptTouchEvent拦截就是重点
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
ensureTarget();
final int action = MotionEventCompat.getActionMasked(ev);
if (mReturningToStart && action == MotionEvent.ACTION_DOWN) {
mReturningToStart = false;
}
if (!isEnabled() || mReturningToStart || canChildScrollUp()
|| mRefreshing || mNestedScrollInProgress) {
// Fail fast if we're not in a state where a swipe is possible
return false;
}
switch (action) {
case MotionEvent.ACTION_DOWN:
setTargetOffsetTopAndBottom(mOriginalOffsetTop - mCircleView.getTop(), true);
mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
mIsBeingDragged = false;
final float initialDownY = getMotionEventY(ev, mActivePointerId);
if (initialDownY == -1) {
return false;
}
mInitialDownY = initialDownY;
break;
case MotionEvent.ACTION_MOVE:
if (mActivePointerId == INVALID_POINTER) {
Log.e(LOG_TAG, "Got ACTION_MOVE event but don't have an active pointer id.");
return false;
}
final float y = getMotionEventY(ev, mActivePointerId);
if (y == -1) {
return false;
}
final float yDiff = y - mInitialDownY;
if (yDiff > mTouchSlop && !mIsBeingDragged) {
mInitialMotionY = mInitialDownY + mTouchSlop;
mIsBeingDragged = true;
mProgress.setAlpha(STARTING_PROGRESS_ALPHA);
}
break;
case MotionEventCompat.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mIsBeingDragged = false;
mActivePointerId = INVALID_POINTER;
break;
}
return mIsBeingDragged;
}
上面的一系列逻辑,最后mIsBeingDragged就是返回觉得是否拦截事件,我们就在这个方法入手,我们先判断,如果x坐标变化大于y坐标的变化,那么我们就认为这个是左右滑动的,下拉刷新不处理。(也可以做其他判断,这里测试过x>y效果还不错)
我们新建一个自定义view 叫RefreshLayout 继承SwipeRefreshLayout,重新它的onInterceptTouchEvent方法。
(就是下面这几行代码,返回false然后下拉刷新控件不执行onTouchEvent事件)float lastx = 0;
float lasty = 0;
boolean ismovepic = false;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() ==MotionEvent.ACTION_DOWN){
lastx = ev.getX();
lasty = ev.getY();
ismovepic = false;
return super.onInterceptTouchEvent(ev);
}
final int action = MotionEventCompat.getActionMasked(ev);
VLog.v(ev.getX() + "---" + ev.getY());
int x2 = (int) Math.abs(ev.getX() - lastx);
int y2 = (int) Math.abs(ev.getY() - lasty);
//滑动图片最小距离检查
VLog.v("滑动差距 - >" + x2 + "--" + y2);
if (x2>y2){
if (x2>=100)ismovepic = true;
return false;
}
//是否移动图片(下拉刷新不处理)
if (ismovepic){
VLog.v("滑动差距 - >" + x2 + "--" + y2);
return false;
}
boolean isok = super.onInterceptTouchEvent(ev);
VLog.v("isok ->" + isok);
return isok;
}
上面判断,为了效果更好,加入了ismovepic 判断,这个判断也很简单,就是如果我按住图片,然后左右滑动一点点,然后一直下拉,超出了广告条的位置,这样的话,广告条处理不了超出位置的点击事件,就有个bug,下拉刷新会再次出现。所以如果设置它左右滑动后,y坐标变化就算超过100(100可以自由设置,我这里先定100,测试效果还不错),也就是脱离广告条位置,我们也不能让下拉刷新出现。
加入这个判断后,手势划来划去都非常灵敏。
ps:按照这个思路,其实其他的下拉刷新控件,大致的事件冲突解决方案思路也差不多,有更好的方法可以留言讨论一下。
睡觉了,一点多了,技术一点点积累中