事件分发:public boolean dispatchTouchEvent(MotionEvent ev)
当有监听到事件时,首先由Activity的捕获到,进入事件分发处理流程。Activity-->ViewGroup-->View,每一个事件自身都是具有分发事件的能力的,
如果dispatchTouchEvent返回了true,表示改事件在本层不再进行分发事件被自己消费了。至此,事件已经完结。
如果事件分发返回 false,表明事件在本层不再继续进行分发,并交由上层控件的onTouchEvent方法进行消费。
如果事件分发返回系统默认的 super.dispatchTouchEvent(ev),事件将分发给本层的事件拦截onInterceptTouchEvent 方法进行处理。
事件拦截:public boolean onInterceptTouchEvent(MotionEvent ev)
如果 onInterceptTouchEvent 返回 true,则表示将事件进行拦截,并将拦截到的事件交由本层控件 的 onTouchEvent 进行处理;
如果返回结果是false;则表示不对事件进行拦截,事件得以成功分发到子View。并由子View的dispatchTouchEvent进行处理。
如果返回super.onInterceptTouchEvent(ev),事件默认不会被拦截,交由子View的dispatchTouchEvent进行处理。
事件响应:public boolean onTouchEvent(MotionEvent ev)
如果onTouchEvent返回true,表示onTouchEvent处理完事件后消费了此次事件。
如果onTouchEvent返回false,事件在onTouchEvent中处理后DOWN方法,且有上层View的onTouchEvent进行处理MOVE和UP方法。
如果返回super.onTouchEvent(ev),则默认处理的逻辑和返回false时相同。(根据不同的View返回值可能不一样,我用viewpager返回时默认的用super。onTouchEvent(ev)实际上返回的是true,这个只是少数,所以读者们记住默认返回和false相同就行记住这个特例就行了)
总结下:Activity-->ViewGroup-->View当我们点击的时候实际上最外层的Activity最先进行事件分发,默认返回super.dispatchTouchEvent(ev),不拦截事件交给自身的onIntercepTouch(ev)方法执行,默认的话返回super事件也就是false不进行拦截交给ViewGroup的分发事件处理默认返回super.dispatchTouch(ev)处理,同样默认返回super不分发交给onIntercepTouch(ev)处理,默认返回super不拦截又交给View的分发事件处理,因为View的话没有拦截事件所以不会进行拦截所以在View的dispatchTouch(ev)分发中返回的super会默认的执行ontouchEvent(ev),默认的话返回super也就是false不进行处理,交给父类(ViewGroup)的onTouchView处理,默认返回的是super也就是false,又交给父类(Activity)的onTouchView处理
。
接下来就是重头戏了,我在做项目的时候因为需求所以要让ViewPager不能够进行左右滑动,也在网上查询了一系列方法,都是告诉我直接写一个类然后继承ViewPager然后在这个类中重写onTouchEvent方法然后把默认的super改成false就可以了。但是这个时候肯定有读者有疑问了,不是说OntouchEvent方法里面的super和false一样的嘛都是处理下DOWN事件然后回调父类去实现MOVE和UP事件嘛,为什么super能够滑动而false不能滑动呢。这个时候我们通过代码进行观察下。
情形①:
MyViewPage的onTouchEvent直接返回false:
可以看出,直接返回false的结果是,在MyViewPage到达onTouchEvent之后直接交还给activity的onTouchEvent进行处理,而且后续的Move和Up事件不再经过MyViewPage了。
情形②:MyViewPage的onTouchEvent直接返回true:
在Down事件之后,onTouchEvent返回true,消费该系列的事件,故后续的Move和Up事件会在dispatch之后直接交由onTouch处理;
情形③:
MyViewPage的onTouchEvent返回super方法:
相关代码以及log
代码:
boolean b = super.onTouchEvent(event);
Log.d("A","MyViewPage:onTouchEvent--"+b);
return b;
代码上可以看出,其实return返回的也是true,仅仅是多了一个super操作。
下面看下源码做了哪些操作:
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {
// Don't handle edge touches immediately -- they may actually belong to one of our
// descendants.
return false;
}
if (mAdapter == null || mAdapter.getCount() == 0) {
// Nothing to present or scroll; nothing to touch.
return false;
}
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);
final int action = ev.getAction();
boolean needsInvalidate = false;
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
mScroller.abortAnimation();
mPopulatePending = false;
populate();
// Remember where the motion event started
mLastMotionX = mInitialMotionX = ev.getX();
mLastMotionY = mInitialMotionY = ev.getY();
mActivePointerId = ev.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE:
if (!mIsBeingDragged) {
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(pointerIndex);
final float xDiff = Math.abs(x - mLastMotionX);
final float y = ev.getY(pointerIndex);
final float yDiff = Math.abs(y - mLastMotionY);
if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);
if (xDiff > mTouchSlop && xDiff > yDiff) {
if (DEBUG) Log.v(TAG, "Starting drag!");//
mIsBeingDragged = true;
requestParentDisallowInterceptTouchEvent(true);
mLastMotionX = x - mInitialMotionX > 0 ? mInitialMotionX + mTouchSlop :
mInitialMotionX - mTouchSlop;
mLastMotionY = y;
setScrollState(SCROLL_STATE_DRAGGING);
setScrollingCacheEnabled(true);
// Disallow Parent Intercept, just in case
ViewParent parent = getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
}
}
// Not else! Note that mIsBeingDragged can be set above.
if (mIsBeingDragged) {
// Scroll to follow the motion event
final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(activePointerIndex);
needsInvalidate |= performDrag(x);
}
break;
case MotionEvent.ACTION_UP:
if (mIsBeingDragged) {
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
final int initialVelocity = (int) velocityTracker.getXVelocity(mActivePointerId);
mPopulatePending = true;
final float scrollStart = getScrollStart();
final float scrolledPages = scrollStart / getPaddedWidth();
final ItemInfo ii = infoForFirstVisiblePage();
final int currentPage = ii.position;
final float nextPageOffset;
if (isLayoutRtl()) {
nextPageOffset = (ii.offset - scrolledPages) / ii.widthFactor;
} else {
nextPageOffset = (scrolledPages - ii.offset) / ii.widthFactor;
}
final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(activePointerIndex);
final int totalDelta = (int) (x - mInitialMotionX);
final int nextPage = determineTargetPage(
currentPage, nextPageOffset, initialVelocity, totalDelta);
setCurrentItemInternal(nextPage, true, true, initialVelocity);
mActivePointerId = INVALID_POINTER;
endDrag();
mLeftEdge.onRelease();
mRightEdge.onRelease();
needsInvalidate = true;
}
break;
case MotionEvent.ACTION_CANCEL:
if (mIsBeingDragged) {
scrollToItem(mCurItem, true, 0, false);
mActivePointerId = INVALID_POINTER;
endDrag();
mLeftEdge.onRelease();
mRightEdge.onRelease();
needsInvalidate = true;
}
break;
case MotionEvent.ACTION_POINTER_DOWN: {
final int index = ev.getActionIndex();
final float x = ev.getX(index);
mLastMotionX = x;
mActivePointerId = ev.getPointerId(index);
break;
}
case MotionEvent.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
mLastMotionX = ev.getX(ev.findPointerIndex(mActivePointerId));
break;
}
if (needsInvalidate) {
postInvalidateOnAnimation();
}
return true;
}
从源码可以看出,在ACTION_MOVE的case中,进行了拖拽的操作。这就是和直接返回true的区别。所以,不论MyViewPage直接返回的是true或者是false,都没进行super里面的拖拽操作。
最后感谢一位大神帮助我解答所有的疑问,其实上诉的结论也都是他得出来的,很感谢为我做的这些。这位大神真的很优秀