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;
- }
@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;
- }
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:按照这个思路,其实其他的下拉刷新控件,大致的事件冲突解决方案思路也差不多,有更好的方法可以留言讨论一下。
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;
- }
@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;
- }
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:按照这个思路,其实其他的下拉刷新控件,大致的事件冲突解决方案思路也差不多,有更好的方法可以留言讨论一下。