对于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的根布局处理,从而实现左右滑动
/**
* 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处理事件