您的位置:首页 > 其它

滑动事件分发和拦截的一点记录

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的根布局处理,从而实现左右滑动

/**
* 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处理事件
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: