SlidingDrawer.java 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707
  1. package net.simonvt.menudrawer;
  2. import android.app.Activity;
  3. import android.content.Context;
  4. import android.graphics.Canvas;
  5. import android.util.AttributeSet;
  6. import android.view.MotionEvent;
  7. import android.view.VelocityTracker;
  8. public class SlidingDrawer extends DraggableDrawer {
  9. private static final String TAG = "OverlayDrawer";
  10. SlidingDrawer(Activity activity, int dragMode) {
  11. super(activity, dragMode);
  12. }
  13. public SlidingDrawer(Context context) {
  14. super(context);
  15. }
  16. public SlidingDrawer(Context context, AttributeSet attrs) {
  17. super(context, attrs);
  18. }
  19. public SlidingDrawer(Context context, AttributeSet attrs, int defStyle) {
  20. super(context, attrs, defStyle);
  21. }
  22. @Override
  23. protected void initDrawer(Context context, AttributeSet attrs, int defStyle) {
  24. super.initDrawer(context, attrs, defStyle);
  25. super.addView(mMenuContainer, -1, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
  26. super.addView(mContentContainer, -1, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
  27. }
  28. @Override
  29. public void openMenu(boolean animate) {
  30. int animateTo = 0;
  31. switch (getPosition()) {
  32. case LEFT:
  33. case TOP:
  34. animateTo = mMenuSize;
  35. break;
  36. case RIGHT:
  37. case BOTTOM:
  38. animateTo = -mMenuSize;
  39. break;
  40. }
  41. animateOffsetTo(animateTo, 0, animate);
  42. }
  43. @Override
  44. public void closeMenu(boolean animate) {
  45. animateOffsetTo(0, 0, animate);
  46. }
  47. @Override
  48. protected void onOffsetPixelsChanged(int offsetPixels) {
  49. if (USE_TRANSLATIONS) {
  50. switch (getPosition()) {
  51. case TOP:
  52. case BOTTOM:
  53. mContentContainer.setTranslationY(offsetPixels);
  54. break;
  55. default:
  56. mContentContainer.setTranslationX(offsetPixels);
  57. break;
  58. }
  59. } else {
  60. switch (getPosition()) {
  61. case TOP:
  62. case BOTTOM:
  63. mContentContainer.offsetTopAndBottom(offsetPixels - mContentContainer.getTop());
  64. break;
  65. default:
  66. mContentContainer.offsetLeftAndRight(offsetPixels - mContentContainer.getLeft());
  67. break;
  68. }
  69. }
  70. offsetMenu(offsetPixels);
  71. invalidate();
  72. }
  73. @Override
  74. protected void initPeekScroller() {
  75. switch (getPosition()) {
  76. case RIGHT:
  77. case BOTTOM: {
  78. final int dx = -mMenuSize / 3;
  79. mPeekScroller.startScroll(0, 0, dx, 0, PEEK_DURATION);
  80. break;
  81. }
  82. default: {
  83. final int dx = mMenuSize / 3;
  84. mPeekScroller.startScroll(0, 0, dx, 0, PEEK_DURATION);
  85. break;
  86. }
  87. }
  88. }
  89. @Override
  90. protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  91. super.onSizeChanged(w, h, oldw, oldh);
  92. onOffsetPixelsChanged((int) mOffsetPixels);
  93. }
  94. @Override
  95. protected void drawOverlay(Canvas canvas) {
  96. final int width = getWidth();
  97. final int height = getHeight();
  98. final int offsetPixels = (int) mOffsetPixels;
  99. final float openRatio = Math.abs(mOffsetPixels) / mMenuSize;
  100. switch (getPosition()) {
  101. case LEFT:
  102. mMenuOverlay.setBounds(0, 0, offsetPixels, height);
  103. break;
  104. case RIGHT:
  105. mMenuOverlay.setBounds(width + offsetPixels, 0, width, height);
  106. break;
  107. case TOP:
  108. mMenuOverlay.setBounds(0, 0, width, offsetPixels);
  109. break;
  110. case BOTTOM:
  111. mMenuOverlay.setBounds(0, height + offsetPixels, width, height);
  112. break;
  113. }
  114. mMenuOverlay.setAlpha((int) (MAX_MENU_OVERLAY_ALPHA * (1.f - openRatio)));
  115. mMenuOverlay.draw(canvas);
  116. }
  117. @Override
  118. protected void onLayout(boolean changed, int l, int t, int r, int b) {
  119. final int width = r - l;
  120. final int height = b - t;
  121. if (USE_TRANSLATIONS) {
  122. mContentContainer.layout(0, 0, width, height);
  123. } else {
  124. final int offsetPixels = (int) mOffsetPixels;
  125. if (getPosition() == Position.LEFT || getPosition() == Position.RIGHT) {
  126. mContentContainer.layout(offsetPixels, 0, width + offsetPixels, height);
  127. } else {
  128. mContentContainer.layout(0, offsetPixels, width, height + offsetPixels);
  129. }
  130. }
  131. switch (getPosition()) {
  132. case LEFT:
  133. mMenuContainer.layout(0, 0, mMenuSize, height);
  134. break;
  135. case RIGHT:
  136. mMenuContainer.layout(width - mMenuSize, 0, width, height);
  137. break;
  138. case TOP:
  139. mMenuContainer.layout(0, 0, width, mMenuSize);
  140. break;
  141. case BOTTOM:
  142. mMenuContainer.layout(0, height - mMenuSize, width, height);
  143. break;
  144. }
  145. }
  146. /**
  147. * Offsets the menu relative to its original position based on the position of the content.
  148. *
  149. * @param offsetPixels The number of pixels the content if offset.
  150. */
  151. private void offsetMenu(int offsetPixels) {
  152. if (!mOffsetMenu || mMenuSize == 0) {
  153. return;
  154. }
  155. final int width = getWidth();
  156. final int height = getHeight();
  157. final int menuSize = mMenuSize;
  158. final int sign = (int) (mOffsetPixels / Math.abs(mOffsetPixels));
  159. final float openRatio = Math.abs(mOffsetPixels) / menuSize;
  160. final int offset = (int) (-0.25f * ((1.0f - openRatio) * menuSize) * sign);
  161. switch (getPosition()) {
  162. case LEFT: {
  163. if (USE_TRANSLATIONS) {
  164. if (offsetPixels > 0) {
  165. mMenuContainer.setTranslationX(offset);
  166. } else {
  167. mMenuContainer.setTranslationX(-menuSize);
  168. }
  169. } else {
  170. mMenuContainer.offsetLeftAndRight(offset - mMenuContainer.getLeft());
  171. mMenuContainer.setVisibility(offsetPixels == 0 ? INVISIBLE : VISIBLE);
  172. }
  173. break;
  174. }
  175. case RIGHT: {
  176. if (USE_TRANSLATIONS) {
  177. if (offsetPixels != 0) {
  178. mMenuContainer.setTranslationX(offset);
  179. } else {
  180. mMenuContainer.setTranslationX(menuSize);
  181. }
  182. } else {
  183. final int oldOffset = mMenuContainer.getRight() - width;
  184. final int offsetBy = offset - oldOffset;
  185. mMenuContainer.offsetLeftAndRight(offsetBy);
  186. mMenuContainer.setVisibility(offsetPixels == 0 ? INVISIBLE : VISIBLE);
  187. }
  188. break;
  189. }
  190. case TOP: {
  191. if (USE_TRANSLATIONS) {
  192. if (offsetPixels > 0) {
  193. mMenuContainer.setTranslationY(offset);
  194. } else {
  195. mMenuContainer.setTranslationY(-menuSize);
  196. }
  197. } else {
  198. mMenuContainer.offsetTopAndBottom(offset - mMenuContainer.getTop());
  199. mMenuContainer.setVisibility(offsetPixels == 0 ? INVISIBLE : VISIBLE);
  200. }
  201. break;
  202. }
  203. case BOTTOM: {
  204. if (USE_TRANSLATIONS) {
  205. if (offsetPixels != 0) {
  206. mMenuContainer.setTranslationY(offset);
  207. } else {
  208. mMenuContainer.setTranslationY(menuSize);
  209. }
  210. } else {
  211. final int oldOffset = mMenuContainer.getBottom() - height;
  212. final int offsetBy = offset - oldOffset;
  213. mMenuContainer.offsetTopAndBottom(offsetBy);
  214. mMenuContainer.setVisibility(offsetPixels == 0 ? INVISIBLE : VISIBLE);
  215. }
  216. break;
  217. }
  218. }
  219. }
  220. @Override
  221. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  222. final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
  223. final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
  224. if (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED) {
  225. throw new IllegalStateException("Must measure with an exact size");
  226. }
  227. final int width = MeasureSpec.getSize(widthMeasureSpec);
  228. final int height = MeasureSpec.getSize(heightMeasureSpec);
  229. if (mOffsetPixels == -1) openMenu(false);
  230. int menuWidthMeasureSpec;
  231. int menuHeightMeasureSpec;
  232. switch (getPosition()) {
  233. case TOP:
  234. case BOTTOM:
  235. menuWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, width);
  236. menuHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec, 0, mMenuSize);
  237. break;
  238. default:
  239. // LEFT/RIGHT
  240. menuWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, mMenuSize);
  241. menuHeightMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, height);
  242. }
  243. mMenuContainer.measure(menuWidthMeasureSpec, menuHeightMeasureSpec);
  244. final int contentWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, width);
  245. final int contentHeightMeasureSpec = getChildMeasureSpec(widthMeasureSpec, 0, height);
  246. mContentContainer.measure(contentWidthMeasureSpec, contentHeightMeasureSpec);
  247. setMeasuredDimension(width, height);
  248. updateTouchAreaSize();
  249. }
  250. private boolean isContentTouch(int x, int y) {
  251. boolean contentTouch = false;
  252. switch (getPosition()) {
  253. case LEFT:
  254. contentTouch = ViewHelper.getLeft(mContentContainer) < x;
  255. break;
  256. case RIGHT:
  257. contentTouch = ViewHelper.getRight(mContentContainer) > x;
  258. break;
  259. case TOP:
  260. contentTouch = ViewHelper.getTop(mContentContainer) < y;
  261. break;
  262. case BOTTOM:
  263. contentTouch = ViewHelper.getBottom(mContentContainer) > y;
  264. break;
  265. }
  266. return contentTouch;
  267. }
  268. protected boolean onDownAllowDrag(int x, int y) {
  269. switch (getPosition()) {
  270. case LEFT:
  271. return (!mMenuVisible && mInitialMotionX <= mTouchSize)
  272. || (mMenuVisible && mInitialMotionX >= mOffsetPixels);
  273. case RIGHT:
  274. final int width = getWidth();
  275. final int initialMotionX = (int) mInitialMotionX;
  276. return (!mMenuVisible && initialMotionX >= width - mTouchSize)
  277. || (mMenuVisible && initialMotionX <= width + mOffsetPixels);
  278. case TOP:
  279. return (!mMenuVisible && mInitialMotionY <= mTouchSize)
  280. || (mMenuVisible && mInitialMotionY >= mOffsetPixels);
  281. case BOTTOM:
  282. final int height = getHeight();
  283. return (!mMenuVisible && mInitialMotionY >= height - mTouchSize)
  284. || (mMenuVisible && mInitialMotionY <= height + mOffsetPixels);
  285. }
  286. return false;
  287. }
  288. protected boolean onMoveAllowDrag(int x, int y, float dx, float dy) {
  289. switch (getPosition()) {
  290. case LEFT:
  291. return (!mMenuVisible && mInitialMotionX <= mTouchSize && (dx > 0))
  292. || (mMenuVisible && x >= mOffsetPixels);
  293. case RIGHT:
  294. final int width = getWidth();
  295. return (!mMenuVisible && mInitialMotionX >= width - mTouchSize && (dx < 0))
  296. || (mMenuVisible && x <= width + mOffsetPixels);
  297. case TOP:
  298. return (!mMenuVisible && mInitialMotionY <= mTouchSize && (dy > 0))
  299. || (mMenuVisible && y >= mOffsetPixels);
  300. case BOTTOM:
  301. final int height = getHeight();
  302. return (!mMenuVisible && mInitialMotionY >= height - mTouchSize && (dy < 0))
  303. || (mMenuVisible && y <= height + mOffsetPixels);
  304. }
  305. return false;
  306. }
  307. protected void onMoveEvent(float dx, float dy) {
  308. switch (getPosition()) {
  309. case LEFT:
  310. setOffsetPixels(Math.min(Math.max(mOffsetPixels + dx, 0), mMenuSize));
  311. break;
  312. case RIGHT:
  313. setOffsetPixels(Math.max(Math.min(mOffsetPixels + dx, 0), -mMenuSize));
  314. break;
  315. case TOP:
  316. setOffsetPixels(Math.min(Math.max(mOffsetPixels + dy, 0), mMenuSize));
  317. break;
  318. case BOTTOM:
  319. setOffsetPixels(Math.max(Math.min(mOffsetPixels + dy, 0), -mMenuSize));
  320. break;
  321. }
  322. }
  323. protected void onUpEvent(int x, int y) {
  324. final int offsetPixels = (int) mOffsetPixels;
  325. switch (getPosition()) {
  326. case LEFT: {
  327. if (mIsDragging) {
  328. mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
  329. final int initialVelocity = (int) getXVelocity(mVelocityTracker);
  330. mLastMotionX = x;
  331. animateOffsetTo(initialVelocity > 0 ? mMenuSize : 0, initialVelocity, true);
  332. // Close the menu when content is clicked while the menu is visible.
  333. } else if (mMenuVisible && x > offsetPixels) {
  334. closeMenu();
  335. }
  336. break;
  337. }
  338. case TOP: {
  339. if (mIsDragging) {
  340. mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
  341. final int initialVelocity = (int) getYVelocity(mVelocityTracker);
  342. mLastMotionY = y;
  343. animateOffsetTo(initialVelocity > 0 ? mMenuSize : 0, initialVelocity, true);
  344. // Close the menu when content is clicked while the menu is visible.
  345. } else if (mMenuVisible && y > offsetPixels) {
  346. closeMenu();
  347. }
  348. break;
  349. }
  350. case RIGHT: {
  351. final int width = getWidth();
  352. if (mIsDragging) {
  353. mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
  354. final int initialVelocity = (int) getXVelocity(mVelocityTracker);
  355. mLastMotionX = x;
  356. animateOffsetTo(initialVelocity > 0 ? 0 : -mMenuSize, initialVelocity, true);
  357. // Close the menu when content is clicked while the menu is visible.
  358. } else if (mMenuVisible && x < width + offsetPixels) {
  359. closeMenu();
  360. }
  361. break;
  362. }
  363. case BOTTOM: {
  364. if (mIsDragging) {
  365. mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
  366. final int initialVelocity = (int) getYVelocity(mVelocityTracker);
  367. mLastMotionY = y;
  368. animateOffsetTo(initialVelocity < 0 ? -mMenuSize : 0, initialVelocity, true);
  369. // Close the menu when content is clicked while the menu is visible.
  370. } else if (mMenuVisible && y < getHeight() + offsetPixels) {
  371. closeMenu();
  372. }
  373. break;
  374. }
  375. }
  376. }
  377. protected boolean checkTouchSlop(float dx, float dy) {
  378. switch (getPosition()) {
  379. case TOP:
  380. case BOTTOM:
  381. return Math.abs(dy) > mTouchSlop && Math.abs(dy) > Math.abs(dx);
  382. default:
  383. return Math.abs(dx) > mTouchSlop && Math.abs(dx) > Math.abs(dy);
  384. }
  385. }
  386. public boolean onInterceptTouchEvent(MotionEvent ev) {
  387. final int action = ev.getAction() & MotionEvent.ACTION_MASK;
  388. if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
  389. mActivePointerId = INVALID_POINTER;
  390. mIsDragging = false;
  391. if (mVelocityTracker != null) {
  392. mVelocityTracker.recycle();
  393. mVelocityTracker = null;
  394. }
  395. if (Math.abs(mOffsetPixels) > mMenuSize / 2) {
  396. openMenu();
  397. } else {
  398. closeMenu();
  399. }
  400. return false;
  401. }
  402. if (action == MotionEvent.ACTION_DOWN && mMenuVisible && isCloseEnough()) {
  403. setOffsetPixels(0);
  404. stopAnimation();
  405. endPeek();
  406. setDrawerState(STATE_CLOSED);
  407. mIsDragging = false;
  408. }
  409. // Always intercept events over the content while menu is visible.
  410. if (mMenuVisible) {
  411. int index = 0;
  412. if (mActivePointerId != INVALID_POINTER) {
  413. index = ev.findPointerIndex(mActivePointerId);
  414. index = index == -1 ? 0 : index;
  415. }
  416. final int x = (int) ev.getX(index);
  417. final int y = (int) ev.getY(index);
  418. if (isContentTouch(x, y)) {
  419. return true;
  420. }
  421. }
  422. if (!mMenuVisible && !mIsDragging && mTouchMode == TOUCH_MODE_NONE) {
  423. return false;
  424. }
  425. if (action != MotionEvent.ACTION_DOWN && mIsDragging) {
  426. return true;
  427. }
  428. switch (action) {
  429. case MotionEvent.ACTION_DOWN: {
  430. mLastMotionX = mInitialMotionX = ev.getX();
  431. mLastMotionY = mInitialMotionY = ev.getY();
  432. final boolean allowDrag = onDownAllowDrag((int) mLastMotionX, (int) mLastMotionY);
  433. mActivePointerId = ev.getPointerId(0);
  434. if (allowDrag) {
  435. setDrawerState(mMenuVisible ? STATE_OPEN : STATE_CLOSED);
  436. stopAnimation();
  437. endPeek();
  438. mIsDragging = false;
  439. }
  440. break;
  441. }
  442. case MotionEvent.ACTION_MOVE: {
  443. final int activePointerId = mActivePointerId;
  444. if (activePointerId == INVALID_POINTER) {
  445. // If we don't have a valid id, the touch down wasn't on content.
  446. break;
  447. }
  448. final int pointerIndex = ev.findPointerIndex(activePointerId);
  449. if (pointerIndex == -1) {
  450. mIsDragging = false;
  451. mActivePointerId = INVALID_POINTER;
  452. endDrag();
  453. closeMenu(true);
  454. return false;
  455. }
  456. final float x = ev.getX(pointerIndex);
  457. final float dx = x - mLastMotionX;
  458. final float y = ev.getY(pointerIndex);
  459. final float dy = y - mLastMotionY;
  460. if (checkTouchSlop(dx, dy)) {
  461. if (mOnInterceptMoveEventListener != null && (mTouchMode == TOUCH_MODE_FULLSCREEN || mMenuVisible)
  462. && canChildrenScroll((int) dx, (int) dy, (int) x, (int) y)) {
  463. endDrag(); // Release the velocity tracker
  464. requestDisallowInterceptTouchEvent(true);
  465. return false;
  466. }
  467. final boolean allowDrag = onMoveAllowDrag((int) x, (int) y, dx, dy);
  468. if (allowDrag) {
  469. setDrawerState(STATE_DRAGGING);
  470. mIsDragging = true;
  471. mLastMotionX = x;
  472. mLastMotionY = y;
  473. }
  474. }
  475. break;
  476. }
  477. case MotionEvent.ACTION_POINTER_UP:
  478. onPointerUp(ev);
  479. mLastMotionX = ev.getX(ev.findPointerIndex(mActivePointerId));
  480. mLastMotionY = ev.getY(ev.findPointerIndex(mActivePointerId));
  481. break;
  482. }
  483. if (mVelocityTracker == null) mVelocityTracker = VelocityTracker.obtain();
  484. mVelocityTracker.addMovement(ev);
  485. return mIsDragging;
  486. }
  487. @Override
  488. public boolean onTouchEvent(MotionEvent ev) {
  489. if (!mMenuVisible && !mIsDragging && mTouchMode == TOUCH_MODE_NONE) {
  490. return false;
  491. }
  492. final int action = ev.getAction() & MotionEvent.ACTION_MASK;
  493. if (mVelocityTracker == null) mVelocityTracker = VelocityTracker.obtain();
  494. mVelocityTracker.addMovement(ev);
  495. switch (action) {
  496. case MotionEvent.ACTION_DOWN: {
  497. mLastMotionX = mInitialMotionX = ev.getX();
  498. mLastMotionY = mInitialMotionY = ev.getY();
  499. final boolean allowDrag = onDownAllowDrag((int) mLastMotionX, (int) mLastMotionY);
  500. mActivePointerId = ev.getPointerId(0);
  501. if (allowDrag) {
  502. stopAnimation();
  503. endPeek();
  504. startLayerTranslation();
  505. }
  506. break;
  507. }
  508. case MotionEvent.ACTION_MOVE: {
  509. final int pointerIndex = ev.findPointerIndex(mActivePointerId);
  510. if (pointerIndex == -1) {
  511. mIsDragging = false;
  512. mActivePointerId = INVALID_POINTER;
  513. endDrag();
  514. closeMenu(true);
  515. return false;
  516. }
  517. if (!mIsDragging) {
  518. final float x = ev.getX(pointerIndex);
  519. final float dx = x - mLastMotionX;
  520. final float y = ev.getY(pointerIndex);
  521. final float dy = y - mLastMotionY;
  522. if (checkTouchSlop(dx, dy)) {
  523. final boolean allowDrag = onMoveAllowDrag((int) x, (int) y, dx, dy);
  524. if (allowDrag) {
  525. setDrawerState(STATE_DRAGGING);
  526. mIsDragging = true;
  527. mLastMotionX = x;
  528. mLastMotionY = y;
  529. } else {
  530. mInitialMotionX = x;
  531. mInitialMotionY = y;
  532. }
  533. }
  534. }
  535. if (mIsDragging) {
  536. startLayerTranslation();
  537. final float x = ev.getX(pointerIndex);
  538. final float dx = x - mLastMotionX;
  539. final float y = ev.getY(pointerIndex);
  540. final float dy = y - mLastMotionY;
  541. mLastMotionX = x;
  542. mLastMotionY = y;
  543. onMoveEvent(dx, dy);
  544. }
  545. break;
  546. }
  547. case MotionEvent.ACTION_CANCEL:
  548. case MotionEvent.ACTION_UP: {
  549. int index = ev.findPointerIndex(mActivePointerId);
  550. index = index == -1 ? 0 : index;
  551. final int x = (int) ev.getX(index);
  552. final int y = (int) ev.getY(index);
  553. onUpEvent(x, y);
  554. mActivePointerId = INVALID_POINTER;
  555. mIsDragging = false;
  556. break;
  557. }
  558. case MotionEvent.ACTION_POINTER_DOWN:
  559. final int index = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
  560. >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
  561. mLastMotionX = ev.getX(index);
  562. mLastMotionY = ev.getY(index);
  563. mActivePointerId = ev.getPointerId(index);
  564. break;
  565. case MotionEvent.ACTION_POINTER_UP:
  566. onPointerUp(ev);
  567. mLastMotionX = ev.getX(ev.findPointerIndex(mActivePointerId));
  568. mLastMotionY = ev.getY(ev.findPointerIndex(mActivePointerId));
  569. break;
  570. }
  571. return true;
  572. }
  573. private void onPointerUp(MotionEvent ev) {
  574. final int pointerIndex = ev.getActionIndex();
  575. final int pointerId = ev.getPointerId(pointerIndex);
  576. if (pointerId == mActivePointerId) {
  577. final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
  578. mLastMotionX = ev.getX(newPointerIndex);
  579. mActivePointerId = ev.getPointerId(newPointerIndex);
  580. if (mVelocityTracker != null) {
  581. mVelocityTracker.clear();
  582. }
  583. }
  584. }
  585. }