标签:
桌面的左右滑动功能主要是在PagedView类中实现的,而WorkSpace是PagedView类的子类,所以会继承PagedView中的方法。当我们的手指点击WorkSpace时,首先就会触发PageView中的onInterceptTouchEvent()方法,会根据相应的条件来判断是否对Touch事件进行拦截,如果onInterceptTouchEvent()方法返回为true,则会对Touch事件进行拦截,PageView类的onTouch方法会进行响应从而得到调用。如果返回false,就分两钟情况:
WorkSpace滑动事件:
同时要注意无论在什么情况下触发了WorkSpace滑动的事件,则系统会不断调用computeScroll()方法,我们重写这个方法同时在这个方法中调用刷新界面等操作。
滑动过程中所要注意的主要方法如下,具体见代码注释。
1 //对Touch事件进行拦截 主要用于在拦截各种Touch事件时,设置mTouchState的各种状态 2 @Override 3 public boolean onInterceptTouchEvent(MotionEvent ev) { 4 /* 5 * This method JUST determines whether we want to intercept the motion. 6 * If we return true, onTouchEvent will be called and we do the actual 7 * scrolling there. 8 * 这个方法仅仅决定了我们是否愿意去对滑动事件进行拦截,如果返回为true,则会调用onTouchEvent我们将会在那里进行事件处理 9 */ 10 //对滑动的速率进行跟踪。 11 12 acquireVelocityTrackerAndAddMovement(ev); 13 14 // Skip touch handling if there are no pages to swipe 15 // 如果没有页面,则跳过操作。 16 if (getChildCount() <= 0) return super.onInterceptTouchEvent(ev); 17 18 /* 19 * Shortcut the most recurring case: the user is in the dragging 20 * state and he is moving his finger. We want to intercept this 21 * motion. 22 * shortcut最常见的情况是:用户处于拖动的状态下,同时在移动它的手指,这时候我们需要拦截这个动作。 23 * 24 */ 25 final int action = ev.getAction(); 26 //如果是在MOVE的情况下,则进行Touch事件拦截 27 if ((action == MotionEvent.ACTION_MOVE) && 28 (mTouchState == TOUCH_STATE_SCROLLING)) { 29 return true; 30 } 31 32 switch (action & MotionEvent.ACTION_MASK) { 33 case MotionEvent.ACTION_MOVE: { 34 /* 35 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check 36 * whether the user has moved far enough from his original down touch. 37 * 如果mIsBeingDragged==false ,否则快捷方式应该捕获到该事件,检查一下用户从它点击的地方位移是否足够 38 */ 39 if (mActivePointerId != INVALID_POINTER) { 40 //根据移动的距离判断是翻页还是移动一段位移,同时设置lastMotionX或者mTouchState这些值。同时取消桌面长按事件。 41 determineScrollingStart(ev); 42 break; 43 } 44 // if mActivePointerId is INVALID_POINTER, then we must have missed an ACTION_DOWN 45 // event. in that case, treat the first occurence of a move event as a ACTION_DOWN 46 // i.e. fall through to the next case (don‘t break) 47 // (We sometimes miss ACTION_DOWN events in Workspace because it ignores all events 48 // while it‘s small- this was causing a crash before we checked for INVALID_POINTER) 49 // 如果mActivePointerId 是 INVALID_POINTER,这时候我们应该已经错过了ACTION_DOWN事件。在这种情况下,把 50 // 第一次发生移动的事件当作ACTION——DOWN事件,直接进入下一个情况下。 51 // 我们有时候会错过workspace中的ACTION_DOWN事件,因为在workspace变小的时候会忽略掉所有的事件。 52 } 53 54 case MotionEvent.ACTION_DOWN: { 55 final float x = ev.getX(); 56 final float y = ev.getY(); 57 // Remember location of down touch 58 // 记录按下的位置 59 mDownMotionX = x; 60 mLastMotionX = x; 61 mLastMotionY = y; 62 mLastMotionXRemainder = 0; 63 mTotalMotionX = 0; 64 //Return the pointer identifier associated with a particular pointer data index is this event. 65 //The identifier tells you the actual pointer number associated with the data, 66 //accounting for individual pointers going up and down since the start of the current gesture. 67 //返回和这个事件关联的触点数据id,计算单独点的id会上下浮动,因为手势的起始位置挥发声改变。 68 mActivePointerId = ev.getPointerId(0); 69 mAllowLongPress = true; 70 71 /* 72 * If being flinged and user touches the screen, initiate drag; 73 * otherwise don‘t. mScroller.isFinished should be false when 74 * being flinged. 75 * 如果被拖动同时用户触摸到了屏幕,就开始初始化拖动,否则便不会。 76 * 当拖动完成后mScroller.isFinished就应该设置为false. 77 * 78 */ 79 final int xDist = Math.abs(mScroller.getFinalX() - mScroller.getCurrX()); 80 81 final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop); 82 if (finishedScrolling) { 83 //标记为TOUCH_STATE_REST状态 84 mTouchState = TOUCH_STATE_REST; 85 //取消滚动动画 86 mScroller.abortAnimation(); 87 } else { 88 //状态为TOUCH_STATE_SCROLLING 89 mTouchState = TOUCH_STATE_SCROLLING; 90 } 91 92 // check if this can be the beginning of a tap on the side of the pages 93 // to scroll the current page 94 // 检测此事件是不是开始于点击页面的边缘来对当前页面进行滚动。 95 if (mTouchState != TOUCH_STATE_PREV_PAGE && mTouchState != TOUCH_STATE_NEXT_PAGE) { 96 if (getChildCount() > 0) { 97 //根据触点的点位来判断是否点击到上一页,从而更新相应的状态 98 if (hitsPreviousPage(x, y)) { 99 mTouchState = TOUCH_STATE_PREV_PAGE; 100 } else if (hitsNextPage(x, y)) { 101 mTouchState = TOUCH_STATE_NEXT_PAGE; 102 } 103 } 104 } 105 break; 106 } 107 108 case MotionEvent.ACTION_UP: 109 case MotionEvent.ACTION_CANCEL: 110 //触点不被相应时,所做的动作 111 mTouchState = TOUCH_STATE_REST; 112 mAllowLongPress = false; 113 mActivePointerId = INVALID_POINTER; 114 //释放速率跟踪 115 releaseVelocityTracker(); 116 break; 117 118 case MotionEvent.ACTION_POINTER_UP: 119 onSecondaryPointerUp(ev); 120 releaseVelocityTracker(); 121 break; 122 } 123 124 /* 125 * The only time we want to intercept motion events is if we are in the 126 * drag mode. 127 * 我们唯一会去对移动事件进行拦截的情况时我们在拖动模式下 128 */ 129 if(DEBUG) Log.d(TAG, "onInterceptTouchEvent "+(mTouchState != TOUCH_STATE_REST)); 130 //只要是mTouchState的状态不为TOUCH_STATE_REST,那么就进行事件拦截 131 return mTouchState != TOUCH_STATE_REST; 132 }
onTouchEvent方法,详细见代码注释:
1 @Override 2 public boolean onTouchEvent(MotionEvent ev) { 3 // Skip touch handling if there are no pages to swipe 4 // 如果没有子页面,就直接跳过 5 if (getChildCount() <= 0) return super.onTouchEvent(ev); 6 7 acquireVelocityTrackerAndAddMovement(ev); 8 9 final int action = ev.getAction(); 10 11 switch (action & MotionEvent.ACTION_MASK) { 12 case MotionEvent.ACTION_DOWN: 13 /* 14 * If being flinged and user touches, stop the fling. isFinished 15 * will be false if being flinged. 16 * 如果在滑动的过程中下用户又点击桌面,则取消滑动,从而响应当前的点击。 17 * 在滑动的isFinished将返回false. 18 */ 19 if (!mScroller.isFinished()) { 20 mScroller.abortAnimation(); 21 } 22 23 // Remember where the motion event started 24 mDownMotionX = mLastMotionX = ev.getX(); 25 mLastMotionXRemainder = 0; 26 mTotalMotionX = 0; 27 mActivePointerId = ev.getPointerId(0); 28 //主要用来显示滚动条,表明要开始滚动了,这里可以进行调整,滚动条时逐渐显示还是立刻显示。 29 if (mTouchState == TOUCH_STATE_SCROLLING) { 30 pageBeginMoving(); 31 } 32 break; 33 34 case MotionEvent.ACTION_MOVE: 35 if (mTouchState == TOUCH_STATE_SCROLLING) { 36 // Scroll to follow the motion event 37 final int pointerIndex = ev.findPointerIndex(mActivePointerId); 38 final float x = ev.getX(pointerIndex); 39 final float deltaX = mLastMotionX + mLastMotionXRemainder - x; 40 //总共移动的距离 41 mTotalMotionX += Math.abs(deltaX); 42 43 // Only scroll and update mLastMotionX if we have moved some discrete amount. We 44 // keep the remainder because we are actually testing if we‘ve moved from the last 45 // scrolled position (which is discrete). 46 // 如果我们移动了一小段距离,我们则移动和更新mLastMotionX 。我们保存Remainder变量是因为会检测我们 47 48 //是否是从最后的滚动点位移动的。 49 if (Math.abs(deltaX) >= 1.0f) { 50 mTouchX += deltaX; 51 mSmoothingTime = System.nanoTime() / NANOTIME_DIV; 52 if (!mDeferScrollUpdate) { 53 scrollBy((int) deltaX, 0); 54 if (DEBUG) Log.d(TAG, "onTouchEvent().Scrolling: " + deltaX); 55 } else { 56 invalidate(); 57 } 58 mLastMotionX = x; 59 mLastMotionXRemainder = deltaX - (int) deltaX; 60 } else { 61 //Trigger the scrollbars to draw. When invoked this method starts an animation to fade the 62 //scrollbars out after a default delay. If a subclass provides animated scrolling, 63 //the start delay should equal the duration of the scrolling animation. 64 //触发scrollbar进行绘制。 使用这个方法来启动一个动画来使scrollbars经过一段时间淡出。如果子类提供了滚动的动画,则 65 //延迟的时间等于动画滚动的时间。 66 awakenScrollBars(); 67 } 68 } else { 69 determineScrollingStart(ev); 70 } 71 break; 72 73 case MotionEvent.ACTION_UP: 74 if (mTouchState == TOUCH_STATE_SCROLLING) { 75 final int activePointerId = mActivePointerId; 76 final int pointerIndex = ev.findPointerIndex(activePointerId); 77 final float x = ev.getX(pointerIndex); 78 final VelocityTracker velocityTracker = mVelocityTracker; 79 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); 80 int velocityX = (int) velocityTracker.getXVelocity(activePointerId); 81 final int deltaX = (int) (x - mDownMotionX); 82 final int pageWidth = getScaledMeasuredWidth(getPageAt(mCurrentPage)); 83 // 屏幕的宽度*0.4f 84 boolean isSignificantMove = Math.abs(deltaX) > pageWidth * 85 SIGNIFICANT_MOVE_THRESHOLD; 86 final int snapVelocity = mSnapVelocity; 87 88 mTotalMotionX += Math.abs(mLastMotionX + mLastMotionXRemainder - x); 89 90 boolean isFling = mTotalMotionX > MIN_LENGTH_FOR_FLING && 91 Math.abs(velocityX) > snapVelocity; 92 93 // In the case that the page is moved far to one direction and then is flung 94 // in the opposite direction, we use a threshold to determine whether we should 95 // just return to the starting page, or if we should skip one further. 96 // 这钟情况是页面朝一个方向移动了一段距离,然后又弹回去了。我们使用一个阀值来判断是进行翻页还是返回到初始页面 97 boolean returnToOriginalPage = false; 98 if (Math.abs(deltaX) > pageWidth * RETURN_TO_ORIGINAL_PAGE_THRESHOLD && 99 Math.signum(velocityX) != Math.signum(deltaX) && isFling) { 100 returnToOriginalPage = true; 101 } 102 103 int finalPage; 104 // We give flings precedence over large moves, which is why we short-circuit our 105 // test for a large move if a fling has been registered. That is, a large 106 // move to the left and fling to the right will register as a fling to the right. 107 //朝右移动 108 if (((isSignificantMove && deltaX > 0 && !isFling) || 109 (isFling && velocityX > 0)) && mCurrentPage > 0) { 110 finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage - 1; 111 snapToPageWithVelocity(finalPage, velocityX); 112 //朝左移动 113 } else if (((isSignificantMove && deltaX < 0 && !isFling) || 114 (isFling && velocityX < 0)) && 115 mCurrentPage < getChildCount() - 1) { 116 finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage + 1; 117 snapToPageWithVelocity(finalPage, velocityX); 118 //寻找离屏幕中心最近的页面移动 119 } else { 120 snapToDestination(); 121 } 122 } 123 //直接移动到前一页 124 else if (mTouchState == TOUCH_STATE_PREV_PAGE) { 125 // at this point we have not moved beyond the touch slop 126 // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so 127 // we can just page 128 int nextPage = Math.max(0, mCurrentPage - 1); 129 if (nextPage != mCurrentPage) { 130 snapToPage(nextPage); 131 } else { 132 snapToDestination(); 133 } 134 } 135 //直接移动到下一页 136 else if (mTouchState == TOUCH_STATE_NEXT_PAGE) { 137 // at this point we have not moved beyond the touch slop 138 // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so 139 // we can just page 140 int nextPage = Math.min(getChildCount() - 1, mCurrentPage + 1); 141 if (nextPage != mCurrentPage) { 142 snapToPage(nextPage); 143 } else { 144 snapToDestination(); 145 } 146 } else { 147 onUnhandledTap(ev); 148 } 149 mTouchState = TOUCH_STATE_REST; 150 mActivePointerId = INVALID_POINTER; 151 releaseVelocityTracker(); 152 break; 153 //对事件不响应 154 case MotionEvent.ACTION_CANCEL: 155 if (mTouchState == TOUCH_STATE_SCROLLING) { 156 snapToDestination(); 157 } 158 mTouchState = TOUCH_STATE_REST; 159 mActivePointerId = INVALID_POINTER; 160 releaseVelocityTracker(); 161 break; 162 163 case MotionEvent.ACTION_POINTER_UP: 164 onSecondaryPointerUp(ev); 165 break; 166 } 167 168 return true; 169 }
scrollTo和scrollBy的区别
我们查看View类的源代码如下所示,mScrollX记录的是当前View针对屏幕坐标在水平方向上的偏移量,而mScrollY则是记录的时当前View针对屏幕在竖值方向上的偏移 量。
从以下代码我们可以得知:
我们在上面的代码中可以看到当我们手指不放移动屏幕时,就会调用scrollBy来移动一段相对的距离。而当我们手指松开后,会调用 mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, duration); 来产生一段动画来移动到相应的页面,在这个过程中系统回不断调用computeScroll(),我们再使用scrollTo来把View移动到当前Scroller所在的绝对位置。
1 /** 2 * Set the scrolled position of your view. This will cause a call to 3 * {@link #onScrollChanged(int, int, int, int)} and the view will be 4 * invalidated. 5 * @param x the x position to scroll to 6 * @param y the y position to scroll to 7 */ 8 public void scrollTo(int x, int y) { 9 if (mScrollX != x || mScrollY != y) { 10 int oldX = mScrollX; 11 int oldY = mScrollY; 12 mScrollX = x; 13 mScrollY = y; 14 invalidateParentCaches(); 15 onScrollChanged(mScrollX, mScrollY, oldX, oldY); 16 if (!awakenScrollBars()) { 17 invalidate(true); 18 } 19 } 20 } 21 /** 22 * Move the scrolled position of your view. This will cause a call to 23 * {@link #onScrollChanged(int, int, int, int)} and the view will be 24 * invalidated. 25 * @param x the amount of pixels to scroll by horizontally 26 * @param y the amount of pixels to scroll by vertically 27 */ 28 public void scrollBy(int x, int y) { 29 scrollTo(mScrollX + x, mScrollY + y); 30 }
http://mobile.51cto.com/hot-316799.htm
Android4.0 Launcher 源码分析3——WorkSpace结构
标签:
原文地址:http://www.cnblogs.com/nathan909/p/5478247.html