概述
一个ViewGroup的滑动肯定和它的dispatchTouchEvent()、onInterceptTouchEvent()、onTouchEvent()有关。ViewPager重写了后两者,我们一个个来看。首先说明一下,ViewPager根据手势产生视图移动的方式有两种,一种是MOVE的时候随手指的拖动,一种是UP之后滑动到指定页面,而滑动是通过Scroller + computeScroll()实现的。
onInterceptTouchEvent()
Viewpager有一个mScrollState成员维护着ViewPager当前页面的状态,它可能被赋三个值。
/**
* Indicates that the pager is in an idle, settled state. The current page
* is fully in view and no animation is in progress.
*/
//当前page空闲,没有动画
public static final int SCROLL_STATE_IDLE = 0;
/**
* Indicates that the pager is currently being dragged by the user.
*/
//正在被拖动
public static final int SCROLL_STATE_DRAGGING = 1;
/**
* Indicates that the pager is in the process of settling to a final position.
*/
//正在向最终位置移动
public static final int SCROLL_STATE_SETTLING = 2;
正如onInterceptTouchEvent()注释所说的,方法只是判断我们是否应该拦截这个Touch事件,scrolling都交给onTouchEvent()去做。因为在switch中每一个分支都有break,所以我调换了一下源码的顺序把DOWN放在了MOVE前面,这样更加清晰。
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
/*
* This method JUST determines whether we want to intercept the motion.
* If we return true, onMotionEvent will be called and we do the actual
* scrolling there.
*/
final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
// Always take care of the touch gesture being complete.
//如果一套手势结束,返回false
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
// Release the drag.
if (DEBUG) Log.v(TAG, "Intercept done!");
resetTouch();
return false;
}
// Nothing more to do here if we have decided whether or not we
// are dragging.
if (action != MotionEvent.ACTION_DOWN) {
//如果正在被drag,拦截
if (mIsBeingDragged) {
if (DEBUG) Log.v(TAG, "Intercept returning true!");
return true;
}
//不允许drag,不拦截
if (mIsUnableToDrag) {
if (DEBUG) Log.v(TAG, "Intercept returning false!");
return false;
}
}
switch (action) {
case MotionEvent.ACTION_DOWN: {
/*
* Remember location of down touch.
* ACTION_DOWN always refers to pointer index 0.
*/
//重新给这四个变量赋值,表示一套手势的开始
mLastMotionX = mInitialMotionX = ev.getX();
mLastMotionY = mInitialMotionY = ev.getY();
//获取第一个触摸点的id
mActivePointerId = ev.getPointerId(0);
//设置允许拖拽为false
mIsUnableToDrag = false;
//标记为开始滚动
mIsScrollStarted = true;
//这个Scroller是在initViewPager()中创建的,这里手动调用计算一下 //Scroller中的x,y值
mScroller.computeScrollOffset();
//如果此时正在向终态靠拢,并且离最终位置还有一定距离
if (mScrollState == SCROLL_STATE_SETTLING
&& Math.abs(mScroller.getFinalX() - mScroller.getCurrX()) > mCloseEnough) {
// Let the user 'catch' the pager as it animates.
//让用户抓住这个Pager
//停止滚动
mScroller.abortAnimation();
//???
mPopulatePending = false;
//更新缓存page信息(滚动的时候mCurItem会改变?)
populate();
//表示在拖动
mIsBeingDragged = true;
//不允许父ViewGroup拦截
requestParentDisallowInterceptTouchEvent(true);
//设置新的状态
setScrollState(SCROLL_STATE_DRAGGING);
} else {
//这里我理解的是正在向终态靠近且距离足够小了,所以不能干涉移动
completeScroll(false);
mIsBeingDragged = false;
}
....
break;
}
case MotionEvent.ACTION_MOVE: {
/*
* mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
* whether the user has moved far enough from his original down touch.
*/
//注释的很清楚,如果能进入这个地方说明mIsBeingDragged == false
//这里要检查用户是否已经从原始位置移动的够远,以给mIsBeingDragged赋值
//第一个触摸点的id
final int activePointerId = mActivePointerId;
if (activePointerId == INVALID_POINTER) {
// If we don't have a valid id, the touch down wasn't on content.
break;
}
//不太懂,貌似对第一个触摸点id做了一个转换
final int pointerIndex = ev.findPointerIndex(activePointerId);
//触摸点横坐标
final float x = ev.getX(pointerIndex);
//横向偏移
final float dx = x - mLastMotionX;
//横向偏移绝对值
final float xDiff = Math.abs(dx);
//纵向
final float y = ev.getY(pointerIndex);
final float yDiff = Math.abs(y - mInitialMotionY);
...
//这里是说如果在这个区域子View可以滑动,交给子View处理,不拦截