• 沒有找到結果。

桌面的左右滑动功能主要是在 PagedView 类中实现的,而 WorkSpace 是 PagedView 类的子 类,所以会继承 PagedView 中的方法。当我们的手指点击 WorkSpace 时,首先就会触发 PageView 中的 onInterceptTouchEvent()方法,会根据相应的条件来判断是否对 Touch 事件进行拦截,

如果 onInterceptTouchEvent()方法返回为 true,则会对 Touch 事件进行拦截,PageView 类的 onTouch 方法会进行响应从而得到调用。如果返回 false,就分两钟情况:(1)我们是点击在它 的子控键上进行滑动时,比如我们是点击在桌面的图标上进行左右滑动的, workspace 则会把 Touch 事件分发给它的子控件。(2)而如果仅仅是点击到桌面的空白出 Touch 事件就不会发生响 应。

在我们手指第一次触摸到屏幕时,首先会对 onInterceptTouchEvent 中的事件进行判断,

如果是按下事件(MotionEvent.ACTION_DOWN), 则会记录按下时的 X 坐标、Y 坐标等等数据,同 时改变现在 Workspace 的状态为滚动状态(OUCH_STATE_SCROLLING),这时会返回 ture,把事件 交给 onTouchEvent 函数来处理,onTouchEvent 中同样会对事件类型进行判断,当事件方法为 (otionEvent.ACTION_DOWN)的时候,就可以开始显示滚动的指示条了(就是 Hotseat 上显示第几 屏的屏点)。当我们按着屏幕不放进行滑动的时候,又会在 onInterceptTouchEvent 进行事件拦 截,但是现在的事件类型变为了 MotionEvent.ACTION_MOVE,因为是移动的操作,所以会在拦 截的时候取消桌面长按的事件的响应,同时转到 onTouchEvent 中对 ACTION_MOVE 事件的响应中,

判断我们移动了多少距离,使用 scrollBy 方法来对桌面进行移动,并刷新屏幕。最后我们放开 手后会触发 onTouchEvent 中的 MotionEvent.ACTION_UP 事件,这时会根据滑动的情况来判断是 朝左滑动还是朝右滑动,如果手指只滑动了屏幕宽度的少一半距离,则会弹回原来的页面,滑 动多于屏幕宽度的一半则会进行翻页。同时要注意无论在什么情况下触发了 WorkSpace 滑动的 事件,则系统会不断调用 computeScroll()方法,我们重写这个方法同时在这个方法中调用刷 新界面等操作。

滑动过程中所要注意的主要方法如下,具体见代码注释。

1 //对 Touch 事件进行拦截 主要用于在拦截各种 Touch 事件时,设置 mTouchState 的各种状态 2 @Override

3 publicpublicpublicpublic booleanbooleanbooleanboolean 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,则 会调 用

14 // Skip touch handling if there are no pages to swipe 15 // 如果没有页面,则跳过操作。

16 ifififif (getChildCount() <= 0) returnreturnreturnreturn supersupersupersuper.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 finalfinalfinalfinal intintintint action = ev.getAction();

26 //如果是在 MOVE 的情况下,则进行 Touch 事件拦截 27 ifififif ((action == MotionEvent.ACTION_MOVE) &&

28 (mTouchState == TOUCH_STATE_SCROLLING)) { 29 returnreturnreturnreturn truetruetruetrue;

30 } 31

32 switchswitchswitchswitch (action & MotionEvent.ACTION_MASK) { 33 casecasecasecase 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 ifififif (mActivePointerId != INVALID_POINTER) {

40 //根据移动的距离判断是翻页还是移动一段位移,同时设置 lastMotionX 或者

mTouchState 这些值。同时取消桌面长按事件。

41 determineScrollingStart(ev);

42 breakbreakbreakbreak;

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 casecasecasecase MotionEvent.ACTION_DOWN: { 55 finalfinalfinalfinal floatfloatfloatfloat x = ev.getX();

56 finalfinalfinalfinal floatfloatfloatfloat 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 = truetruetruetrue;

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 finalfinalfinalfinal intintintint xDist = Math.abs(mScroller.getFinalX() -mScroller.getCurrX());

80

81 finalfinalfinalfinal booleanbooleanbooleanbooleanfinishedScrolling = (mScroller.isFinished() || xDist

< mTouchSlop);

82 ifififif (finishedScrolling) {

83 //标记为 TOUCH_STATE_REST 状态 84 mTouchState = TOUCH_STATE_REST;

85 //取消滚动动画

86 mScroller.abortAnimation();

87 } elseelseelseelse {

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 ifififif (mTouchState != TOUCH_STATE_PREV_PAGE && mTouchState !=

TOUCH_STATE_NEXT_PAGE) {

96 ifififif (getChildCount() > 0) {

97 //根据触点的点位来判断是否点击到上一页,从而更新相应的状态

98 ifififif (hitsPreviousPage(x, y)) {

99 mTouchState = TOUCH_STATE_PREV_PAGE;

100 } elseelseelseelse ifififif (hitsNextPage(x, y)) {

101 mTouchState = TOUCH_STATE_NEXT_PAGE;

102 }

103 }

104 }

105 breakbreakbreakbreak;

106 }

107

108 casecasecasecase MotionEvent.ACTION_UP:

109 casecasecasecase MotionEvent.ACTION_CANCEL:

110 //触点不被相应时,所做的动作

111 mTouchState = TOUCH_STATE_REST;

112 mAllowLongPress = falsefalsefalsefalse;

113 mActivePointerId = INVALID_POINTER;

114 //释放速率跟踪

115 releaseVelocityTracker();

116 breakbreakbreakbreak;

117

118 casecasecasecase MotionEvent.ACTION_POINTER_UP:

119 onSecondaryPointerUp(ev);

120 releaseVelocityTracker();

121 breakbreakbreakbreak;

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 ifififif(DEBUG) Log.d(TAG, "onInterceptTouchEvent "+(mTouchState !=

TOUCH_STATE_REST));

130 //只要是 mTouchState 的状态不为 TOUCH_STATE_REST,那么就进行事件拦截 131 returnreturnreturnreturn mTouchState != TOUCH_STATE_REST;

132}

onTouchEvent 方法,详细见代码注释:

133@Override

134publicpublicpublicpublic booleanbooleanbooleanboolean onTouchEvent(MotionEvent ev) {

135 // Skip touch handling if there are no pages to swipe 136 // 如果没有子页面,就直接跳过

137 ifififif (getChildCount() <= 0) returnreturnreturnreturn supersupersupersuper.onTouchEvent(ev);

138

139 acquireVelocityTrackerAndAddMovement(ev);

140

141 finalfinalfinalfinal intintintint action = ev.getAction();

142

143 switchswitchswitchswitch (action & MotionEvent.ACTION_MASK) { 144 casecasecasecase MotionEvent.ACTION_DOWN:

145 /*

146 * If being flinged and user touches, stop the fling. isFinished 147 * will be false if being flinged.

148 * 如果在滑动的过程中下用户又点击桌面,则取消滑动,从而响应当前的点击。

149 * 在滑动的 isFinished 将返回 false.

150 */

151 ifififif (!mScroller.isFinished()) { 152 mScroller.abortAnimation();

153 }

154

155 // Remember where the motion event started 156 mDownMotionX = mLastMotionX = ev.getX();

157 mLastMotionXRemainder = 0;

158 mTotalMotionX = 0;

159 mActivePointerId = ev.getPointerId(0);

160 //主要用来显示滚动条,表明要开始滚动了,这里可以进行调整,滚动条时逐渐显示还是立

刻显示。

161 ifififif (mTouchState == TOUCH_STATE_SCROLLING) { 162 pageBeginMoving();

163 }

164 breakbreakbreakbreak;

165

166 casecasecasecase MotionEvent.ACTION_MOVE:

167 ifififif (mTouchState == TOUCH_STATE_SCROLLING) { 168 // Scroll to follow the motion event

169 finalfinalfinalfinal intintintint pointerIndex = ev.findPointerIndex(mActivePointerId);

170 finalfinalfinalfinal floatfloatfloatfloat x = ev.getX(pointerIndex);

171 finalfinalfinalfinal floatfloatfloatfloat deltaX = mLastMotionX + mLastMotionXRemainder - x;

172 //总共移动的距离

173 mTotalMotionX += Math.abs(deltaX);

174

175 // Only scroll and update mLastMotionX if we have moved some discrete amount. We

176 // keep the remainder because we are actually testing if we've moved from the last

177 // scrolled position (which is discrete).

178 // 如 果 我 们 移 动 了 一 小 段 距 离 , 我 们 则 移 动 和 更 新 mLastMotionX 。 我 们 保 存 Remainder 变量是因为会检测我们

179

180 //是否是从最后的滚动点位移动的。

181 ifififif (Math.abs(deltaX) >= 1.0f) {

182 mTouchX += deltaX;

183 mSmoothingTime = System.nanoTime() / NANOTIME_DIV;

184 ifififif (!mDeferScrollUpdate) { 185 scrollBy((intintintint) deltaX, 0);

186 ifififif (DEBUG) Log.d(TAG, "onTouchEvent().Scrolling: " + deltaX);

187 } elseelseelseelse {

188 invalidate();

189 }

190 mLastMotionX = x;

191 mLastMotionXRemainder = deltaX - (intintintint) deltaX;

192 } elseelseelseelse {

193 //Trigger the scrollbars to draw. When invoked this method starts an animation to fade the

194 //scrollbars out after a default delay. If a subclass provides animated scrolling,

195 //the start delay should equal the duration of the scrolling animation.

196 //触发 scrollbar 进行绘制。 使用这个方法来启动一个动画来使 scrollbars 经过

203 breakbreakbreakbreak;

204

205 casecasecasecase MotionEvent.ACTION_UP:

206 ifififif (mTouchState == TOUCH_STATE_SCROLLING) { 207 finalfinalfinalfinal intintintint activePointerId = mActivePointerId;

208 finalfinalfinalfinal intintintint pointerIndex = ev.findPointerIndex(activePointerId);

209 finalfinalfinalfinal floatfloatfloatfloat x = ev.getX(pointerIndex);

210 finalfinalfinalfinal VelocityTracker velocityTracker = mVelocityTracker;

211 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);

212 intintintint velocityX = (intintintint)

velocityTracker.getXVelocity(activePointerId);

213 finalfinalfinalfinal intintintint deltaX = (intintintint) (x - mDownMotionX);

214 finalfinalfinalfinal intintintint pageWidth =

getScaledMeasuredWidth(getPageAt(mCurrentPage));

215 // 屏幕的宽度*0.4f

216 booleanbooleanbooleanboolean isSignificantMove = Math.abs(deltaX) > pageWidth *

217 SIGNIFICANT_MOVE_THRESHOLD;

218 finalfinalfinalfinal intintintint snapVelocity = mSnapVelocity;

219

220 mTotalMotionX += Math.abs(mLastMotionX + mLastMotionXRemainder -x);

221

222 booleanbooleanbooleanboolean isFling = mTotalMotionX > MIN_LENGTH_FOR_FLING &&

223 Math.abs(velocityX) > snapVelocity;

224

225 // In the case that the page is moved far to one direction and then is flung

226 // in the opposite direction, we use a threshold to determine whether we should

227 // just return to the starting page, or if we should skip one further.

228 // 这钟情况是页面朝一个方向移动了一段距离,然后又弹回去了。我们使用一个阀值

来判断是进行翻页还是返回到初始页面

229 booleanbooleanbooleanboolean returnToOriginalPage = falsefalsefalsefalse;

230 ifififif (Math.abs(deltaX) > pageWidth *

RETURN_TO_ORIGINAL_PAGE_THRESHOLD &&

231 Math.signum(velocityX) != Math.signum(deltaX) && isFling) {

232 returnToOriginalPage = truetruetruetrue;

233 }

234

235 intintintint finalPage;

236 // We give flings precedence over large moves, which is why we short-circuit our

237 // test for a large move if a fling has been registered. That is, a large

238 // move to the left and fling to the right will register as a fling to the right.

239 //朝右移动

240 ifififif (((isSignificantMove && deltaX > 0 && !isFling) ||

241 (isFling && velocityX > 0)) && mCurrentPage > 0) {

242 finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage - 1;

243 snapToPageWithVelocity(finalPage, velocityX);

244 //朝左移动

245 } elseelseelseelse ifififif (((isSignificantMove && deltaX < 0 && !isFling) ||

246 (isFling && velocityX < 0)) &&

247 mCurrentPage < getChildCount() - 1) {

248 finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage + 1;

249 snapToPageWithVelocity(finalPage, velocityX);

250 //寻找离屏幕中心最近的页面移动

256 elseelseelseelse ifififif (mTouchState == TOUCH_STATE_PREV_PAGE) {

257 // at this point we have not moved beyond the touch slop 258 // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so 259 // we can just page

260 intintintint nextPage = Math.max(0, mCurrentPage - 1);

261 ifififif (nextPage != mCurrentPage) { 262 snapToPage(nextPage);

268 elseelseelseelse ifififif (mTouchState == TOUCH_STATE_NEXT_PAGE) {

269 // at this point we have not moved beyond the touch slop 270 // (otherwise mTouchState would be TOUCH_STATE_SCROLLING), so 271 // we can just page

272 intintintint nextPage = Math.min(getChildCount() - 1, mCurrentPage + 1);

273 ifififif (nextPage != mCurrentPage) { 274 snapToPage(nextPage);

275 } elseelseelseelse {

276 snapToDestination();

277 }

278 } elseelseelseelse {

279 onUnhandledTap(ev);

280 }

281 mTouchState = TOUCH_STATE_REST;

282 mActivePointerId = INVALID_POINTER;

283 releaseVelocityTracker();

284 breakbreakbreakbreak;

285 //对事件不响应

286 casecasecasecase MotionEvent.ACTION_CANCEL:

287 ifififif (mTouchState == TOUCH_STATE_SCROLLING) { 288 snapToDestination();

289 }

290 mTouchState = TOUCH_STATE_REST;

291 mActivePointerId = INVALID_POINTER;

292 releaseVelocityTracker();

293 breakbreakbreakbreak;

294

295 casecasecasecase MotionEvent.ACTION_POINTER_UP:

296 onSecondaryPointerUp(ev);

297 breakbreakbreakbreak;

298 } 299

300 returnreturnreturnreturn truetruetruetrue;

301}

最后有个小知识点要搞清楚,不少网友都问到过我。就是 scrollTo 和 scrollBy 的区别。

我们查看 View 类的源代码如下所示,mScrollX 记录的是当前 View 针对屏幕坐标在水平方向上 的偏移量,而 mScrollY 则是记录的时当前 View 针对屏幕在竖值方向上的偏移量。

从以下代码我们可以得知,scrollTo 就是把 View 移动到屏幕的 X 和 Y 位置,也就是绝对 位置。而 scrollBy 其实就是调用的 scrollTo,但是参数是当前 mScrollX 和 mScrollY 加上 X 和 Y 的位置,所以 ScrollBy 调用的是相对于 mScrollX 和 mScrollY 的位置。我们在上面的代码 中可以看到当我们手指不放移动屏幕时,就会调用 scrollBy 来移动一段相对的距离。而当我们 手指松开后,会调用 mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, duration);

来产生一段动画来移动到相应的页面,在这个过程中系统回不断调用 computeScroll(),我们 再使用 scrollTo 来把 View 移动到当前 Scroller 所在的绝对位置。

302/**

303 * Set the scrolled position of your view. This will cause a call to 304 * {@link #onScrollChanged(int, int, int, int)} and the view will be 305 * invalidated.

306 * @param x the x position to scroll to 307 * @param y the y position to scroll to

308 */

309 publicpublicpublicpublic voidvoidvoidvoid scrollTo(intintintint x, intintintint y) { 310 ifififif (mScrollX != x || mScrollY != y) { 311 intintintint oldX = mScrollX;

312 intintintint oldY = mScrollY;

313 mScrollX = x;

314 mScrollY = y;

315 invalidateParentCaches();

316 onScrollChanged(mScrollX, mScrollY, oldX, oldY);

317 ifififif (!awakenScrollBars()) { 318 invalidate(truetruetruetrue);

319 }

320 } 321 } 322 /**

323 * Move the scrolled position of your view. This will cause a call to 324 * {@link #onScrollChanged(int, int, int, int)} and the view will be 325 * invalidated.

326 * @param x the amount of pixels to scroll by horizontally 327 * @param y the amount of pixels to scroll by vertically 328 */

329 publicpublicpublicpublic voidvoidvoidvoid scrollBy(intintintint x, intintintint y) { 330 scrollTo(mScrollX + x, mScrollY + y);

331 }

【编辑推荐】

相關文件