滑动事件分发和拦截的一点记录
2017-12-12 23:50
483 查看
对于listView,如果它的item可以左右滑动,此时的事件分发分析:
listView继承自AbsListView,它的onInterceptTouchEvent默认返回true,所以在move事件时可以滑动
当它的item可以左右滑动时,根据事件分发的流程,若item的根布局的onInterceptTouchEvent,在move事件时,判断左右有位移时返回true,那么就会拦截事件传递,不响应item里的子view的点击事件,而是响应item根布局的onTouchEvent。进入onTouchEvent后,如果按住进行上下滑动,那么会因为listView的onInterceptTouchEvent默认返回true
从而被listview拦截掉,若想进行左右滑动,则要在move事件里判断左右位移是否大于竖直位移,如果大于,则getParent().requestDisallowInterceptTouchEvent(true);
进行反拦截,将touchevent事件交给item的根布局处理,从而实现左右滑动
对于RecyclerView,我们可以来看下源码:
有这么三种状态:空闲,手指拖动,拖动放开后自然滑行
初始状态为空闲:
然后进入到onInterceptTouchEven方法中去看看,
有两个判断滑动方向的值,默认为false.它会根据你给RecyclerView设置的layoutManager来改变,如果设置的时竖直方向的,那么canScrollVertically()就会返回true.
然后接着看,进入down事件,会看到在这里判断和设置RecyclerView的状态,这里就是说,如果RecyclerView是自己靠惯性滑动时,你按下此时会进行反拦截,也就是touch事件时交由RecyclerView自己处理掉了,并且设置状态为滑动状态。
在move事件里,也会判断状态。如果不是滑动状态,即是从空闲状态开始拖动,或者惯性滑动时没有触发down事件,或者惯性滑动后回复空闲状态,此时就会判断RecyclerView是横着滑动还是竖直滑动,不管是什么方向的都会设置startRoll为true,那么就会使RecyclerView的状态变为滑动状态
在UP事件中没有与RecyclerView状态相关的代码,所以可以忽略
在onInterceptTouchEven方法最后返回的是一个判断,判断当前状态是否是滑动状态,如果是则返回true,即拦截子view的事件,自己处理
那么由DOWN和MOVE事件可看出:
空闲状态,点下去不滑动然后松开,mScrollState不为SCROLL_STATE_DRAGGING,子view处理事件
空闲状态,点下去滑动,触发move事件,状态最后变为SCROLL_STATE_DRAGGING,RecyclerView处理事件
惯性状态,点下去,状态变为SCROLL_STATE_DRAGGING,RecyclerView处理事件
listView继承自AbsListView,它的onInterceptTouchEvent默认返回true,所以在move事件时可以滑动
当它的item可以左右滑动时,根据事件分发的流程,若item的根布局的onInterceptTouchEvent,在move事件时,判断左右有位移时返回true,那么就会拦截事件传递,不响应item里的子view的点击事件,而是响应item根布局的onTouchEvent。进入onTouchEvent后,如果按住进行上下滑动,那么会因为listView的onInterceptTouchEvent默认返回true
从而被listview拦截掉,若想进行左右滑动,则要在move事件里判断左右位移是否大于竖直位移,如果大于,则getParent().requestDisallowInterceptTouchEvent(true);
进行反拦截,将touchevent事件交给item的根布局处理,从而实现左右滑动
/** * true:拦截孩子的事件,但会执行当前控件的onTouchEvent()方法 * false:不拦截孩子的事件,事件继续传递 * @param event * @return */ @Override public boolean onInterceptTouchEvent(MotionEvent event) { boolean intercept = false; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //1.按下记录坐标 downX = startX = event.getX(); Log.e(TAG,"SlideLayout-onTouchEvent-ACTION_DOWN"); if(onStateChangeListenter != null){ onStateChangeListenter.onDown(this); } break; case MotionEvent.ACTION_MOVE: Log.e(TAG,"SlideLayout-onTouchEvent-ACTION_MOVE"); //2.记录结束值 float endX = event.getX(); float endY = event.getY(); //3.计算偏移量 float distanceX = endX - startX; startX = event.getX(); //在X轴和Y轴滑动的距离 float DX = Math.abs(endX-downX); if(DX>8){ intercept = true; } break; case MotionEvent.ACTION_UP: break; } return intercept; }
@Override public boolean onTouchEvent(MotionEvent event) { super.onTouchEvent(event); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //1.按下记录坐标 downX = startX = event.getX(); downY = startY = event.getY(); Log.e(TAG,"SlideLayout-onTouchEvent-ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.e(TAG,"SlideLayout-onTouchEvent-ACTION_MOVE"); //2.记录结束值 float endX = event.getX(); float endY = event.getY(); //3.计算偏移量 float distanceX = endX - startX; int toScrollX = (int) (getScrollX() - distanceX); if (toScrollX < 0) { toScrollX = 0; } else if (toScrollX > menuWidth) { toScrollX = menuWidth; } scrollTo(toScrollX, getScrollY()); startX = event.getX(); startY = event.getY(); //在X轴和Y轴滑动的距离 float DX = Math.abs(endX-downX); float DY = Math.abs(endY-downY); if(DX > DY&&DX>8){ //水平方向滑动 //响应侧滑 //反拦截-事件给SlideLayout //listview recyclerview默认会拦截,所以这里要反拦截 getParent().requestDisallowInterceptTouchEvent(true); } break; case MotionEvent.ACTION_UP: Log.e(TAG,"SlideLayout-onTouchEvent-ACTION_UP"); int totalScrollX = getScrollX();//偏移量 if(totalScrollX < menuWidth/2){ //关闭Menu closeMenu(); }else{ //打开Menu openMenu(); } break; } return true; }
对于RecyclerView,我们可以来看下源码:
有这么三种状态:空闲,手指拖动,拖动放开后自然滑行
/** * The RecyclerView is not currently scrolling. * @see #getScrollState() */ public static final int SCROLL_STATE_IDLE = 0; /** * The RecyclerView is currently being dragged by outside input such as user touch input. * @see #getScrollState() */ public static final int SCROLL_STATE_DRAGGING = 1; /** * The RecyclerView is currently animating to a final position while not under * outside control. * @see #getScrollState() */ public static final int SCROLL_STATE_SETTLING = 2;
初始状态为空闲:
private int mScrollState = SCROLL_STATE_IDLE;
然后进入到onInterceptTouchEven方法中去看看,
有两个判断滑动方向的值,默认为false.它会根据你给RecyclerView设置的layoutManager来改变,如果设置的时竖直方向的,那么canScrollVertically()就会返回true.
final boolean canScrollHorizontally = mLayout.canScrollHorizontally(); final boolean canScrollVertically = mLayout.canScrollVertically();
/** * Query if horizontal scrolling is currently supported. The default implementation * returns false. * * @return True if this LayoutManager can scroll the current contents horizontally */ public boolean canScrollHorizontally() { return false; } /** * Query if vertical scrolling is currently supported. The default implementation * returns false. * * @return True if this LayoutManager can scroll the current contents vertically */ public boolean canScrollVertically() { return false; }
然后接着看,进入down事件,会看到在这里判断和设置RecyclerView的状态,这里就是说,如果RecyclerView是自己靠惯性滑动时,你按下此时会进行反拦截,也就是touch事件时交由RecyclerView自己处理掉了,并且设置状态为滑动状态。
if (mScrollState == SCROLL_STATE_SETTLING) { getParent().requestDisallowInterceptTouchEvent(true); setScrollState(SCROLL_STATE_DRAGGING); }
在move事件里,也会判断状态。如果不是滑动状态,即是从空闲状态开始拖动,或者惯性滑动时没有触发down事件,或者惯性滑动后回复空闲状态,此时就会判断RecyclerView是横着滑动还是竖直滑动,不管是什么方向的都会设置startRoll为true,那么就会使RecyclerView的状态变为滑动状态
case MotionEvent.ACTION_MOVE: { final int index = e.findPointerIndex(mScrollPointerId); if (index < 0) { Log.e(TAG, "Error processing scroll; pointer index for id " + mScrollPointerId + " not found. Did any MotionEvents get skipped?"); return false; } final int x = (int) (e.getX(index) + 0.5f); final int y = (int) (e.getY(index) + 0.5f); if (mScrollState != SCROLL_STATE_DRAGGING) { final int dx = x - mInitialTouchX; final int dy = y - mInitialTouchY; boolean startScroll = false; if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) { mLastTouchX = mInitialTouchX + mTouchSlop * (dx < 0 ? -1 : 1); startScroll = true; } if (canScrollVertically && Math.abs(dy) > mTouchSlop) { mLastTouchY = mInitialTouchY + mTouchSlop * (dy < 0 ? -1 : 1); startScroll = true; } if (startScroll) { setScrollState(SCROLL_STATE_DRAGGING); } } } break;
在UP事件中没有与RecyclerView状态相关的代码,所以可以忽略
在onInterceptTouchEven方法最后返回的是一个判断,判断当前状态是否是滑动状态,如果是则返回true,即拦截子view的事件,自己处理
return mScrollState == SCROLL_STATE_DRAGGING;
那么由DOWN和MOVE事件可看出:
空闲状态,点下去不滑动然后松开,mScrollState不为SCROLL_STATE_DRAGGING,子view处理事件
空闲状态,点下去滑动,触发move事件,状态最后变为SCROLL_STATE_DRAGGING,RecyclerView处理事件
惯性状态,点下去,状态变为SCROLL_STATE_DRAGGING,RecyclerView处理事件
相关文章推荐
- Android从零开搞系列:自定义View(9)事件分发+事件拦截(滑动冲突)
- 【Android View事件分发机制】滑动冲突
- Android View的事件分发机制和滑动冲突解决方案
- Android事件分发及拦截机制
- android View事件分发和事件拦截
- View事件分发记录
- 菜鸟都能理解的Android中View的事件分发机制及滑动冲突处理
- 【Android View事件分发机制】关于拦截事件的注意点
- android事件分发与滑动冲突
- 模仿一个投票,学习一下View的事件分发和拦截
- 事件分发机制与滑动冲突
- react-native Touch事件的拦截与分发
- 事件分发拦截机制
- [置顶] Android开发知识(七):Android事件处理机制:事件分发、传递、拦截、处理机制的原理分析(上)
- viewGroup事件分发记录
- 一步步自定义下拉刷新上拉加载——事件分发与滑动冲突
- Android 利用事件分发实现滑动Activity返回
- Android事件机制之ViewPager的分发与拦截
- Android中View的事件体系(2)——View滑动与事件分发
- Android事件分发,拦截,处理机制