歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Android系統中長按事件的實現機制解析

Android系統中長按事件的實現機制解析

日期:2017/3/1 10:13:07   编辑:Linux編程

在Android的觸摸消息中,已經實現了三種監測,它們分別是

1)pre-pressed:對應的語義是用戶輕觸(tap)了屏幕

2)pressed:對應的語義是用戶點擊(press)了屏幕

3)long pressed:對應的語義是用戶長按(long press)了屏幕

下圖是觸摸消息隨時間變化的時間軸示意圖:


其中,t0和t1定義在ViewConfiguration類中,標識了tap和longpress的超時時間,定義如下:

  1. /**
  2. * Defines the duration in milliseconds we will wait to see if a touch event
  3. * is a tap or a scroll. If the user does not move within this interval, it is
  4. * considered to be a tap.
  5. */
  6. private static final int TAP_TIMEOUT = 115; // t0
  7. /**
  8. * Defines the duration in milliseconds before a press turns into
  9. * a long press
  10. */
  11. private static final int LONG_PRESS_TIMEOUT = 500; // t1
代碼中實現監測的地方在View類的OnTouchEvent函數中,當View監測到ACTION_DOWN事件時,首先發送一個延遲為t0的異步消息,代碼如下:
  1. case MotionEvent.ACTION_DOWN:
  2. if (mPendingCheckForTap == null) {
  3. mPendingCheckForTap = new CheckForTap();
  4. }
  5. mPrivateFlags |= PREPRESSED;
  6. mHasPerformedLongPress = false;
  7. postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
  8. break;
如果在t0時間內用戶釋放了屏幕,即ACTION_UP事件在t0時間內發生,則本次觸摸對應的是pre-pressed處理代碼,語義是"用戶輕觸(TAP)了一下屏幕";如果用戶在t1時間內釋放了屏幕,那麼本次操作是一個"press"操作;如果用戶超過t1時間釋放屏幕,則系統認為監測到了長按事件。其中處理"press"操作的代碼在類CheckForTap類中,處理長按操作的代碼在類CheckForLongPress類中。而處理pre-pressed的代碼在ACTION_UP事件響應中,ACTION_UP事件響應中大部分代碼用於處理觸摸的狀態變化,如下所示:
  1. case MotionEvent.ACTION_UP:
  2. boolean prepressed = (mPrivateFlags & PREPRESSED) != 0; //獲取prepressed狀態
  3. if ((mPrivateFlags & PRESSED) != 0 || prepressed) { //如果是pressed狀態或者是prepressed狀態,才進行處理
  4. // 如果當前view不具有焦點,則需要先獲取焦點,因為我們當前處理觸摸模式
  5. boolean focusTaken = false;
  6. if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
  7. focusTaken = requestFocus(); // 請求獲得焦點
  8. }
  9. if (!mHasPerformedLongPress) { // 是否處理過長按操作了,如果是,則直接返回
  10. // 進入該代碼段,說明這是一個tap操作,首先移除長按回調操作
  11. removeLongPressCallback();
  12. // Only perform take click actions if we were in the pressed state
  13. if (!focusTaken) {
  14. // Use a Runnable and post this rather than calling
  15. // performClick directly. This lets other visual state
  16. // of the view update before click actions start.
  17. if (mPerformClick == null) {
  18. mPerformClick = new PerformClick();
  19. }
  20. if (!post(mPerformClick)) {
  21. performClick(); // 執行點擊的處理函數
  22. }
  23. }
  24. }
  25. if (mUnsetPressedState == null) {
  26. mUnsetPressedState = new UnsetPressedState();
  27. }
  28. if (prepressed) {
  29. mPrivateFlags |= PRESSED;
  30. refreshDrawableState();
  31. // 發送重置觸摸狀態的異步延遲消息
  32. postDelayed(mUnsetPressedState,
  33. ViewConfiguration.getPressedStateDuration());
  34. } else if (!post(mUnsetPressedState)) {
  35. // If the post failed, unpress right now
  36. mUnsetPressedState.run();
  37. }
  38. removeTapCallback(); // 移除tap的回調操作
  39. }
  40. break;
在上面的代碼分析中,可以看出,整個監測過程涉及到兩個Runnable對象和一個利用Handler發送異步延遲消息的函數,下面就來分析一下:

1)PostDelayed函數

該函數的主要工作是獲取UI線程的Handler對象,然後調用Handler類的postDelayed函數將指定的Runnable對象放到消息隊列中。

  1. public boolean postDelayed(Runnable action, long delayMillis) {
  2. Handler handler;
  3. if (mAttachInfo != null) {
  4. handler = mAttachInfo.mHandler;
  5. } else {
  6. // Assume that post will succeed later
  7. ViewRoot.getRunQueue().postDelayed(action, delayMillis);
  8. return true;
  9. }
  10. return handler.postDelayed(action, delayMillis);
  11. }
2)CheckForTap類

該類實現了Runnable接口,在run函數中設置觸摸標識,並刷新Drawable的狀態,同時用於發送一個檢測長按事件的異步延遲消息,代碼如下:

  1. private final class CheckForTap implements Runnable {
  2. public void run() {
  3. // 進入該函數,說明已經過了ViewConfiguration.getTapTimeout()時間,
  4. // 即pre-pressed狀態結束,宣告觸摸進入pressed狀態
  5. mPrivateFlags &= ~PREPRESSED;
  6. mPrivateFlags |= PRESSED;
  7. refreshDrawableState(); // 刷新控件的背景Drawable
  8. // 如果長按檢測沒有被去使能,則發送一個檢測長按事件的異步延遲消息
  9. if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {
  10. postCheckForLongClick(ViewConfiguration.getTapTimeout());
  11. }
  12. }
  13. }
  14. private void postCheckForLongClick(int delayOffset) {
  15. mHasPerformedLongPress = false;
  16. // 實例化CheckForLongPress對象
  17. if (mPendingCheckForLongPress == null) {
  18. mPendingCheckForLongPress = new CheckForLongPress();
  19. }
  20. mPendingCheckForLongPress.rememberWindowAttachCount();
  21. // 調用PostDelayed函數發送長按事件的異步延遲消息
  22. postDelayed(mPendingCheckForLongPress,
  23. ViewConfiguration.getLongPressTimeout() - delayOffset);
  24. }

3)CheckForLongPress類

該類定義了長按操作發生時的響應處理,同樣實現了Runnable接口

  1. class CheckForLongPress implements Runnable {
  2. private int mOriginalWindowAttachCount;
  3. public void run() {
  4. // 進入該函數,說明檢測到了長按操作
  5. if (isPressed() && (mParent != null)
  6. && mOriginalWindowAttachCount == mWindowAttachCount) {
  7. if (performLongClick()) {
  8. mHasPerformedLongPress = true;
  9. }
  10. }
  11. }
  12. public void rememberWindowAttachCount() {
  13. mOriginalWindowAttachCount = mWindowAttachCount;
  14. }
  15. }
  16. public boolean performLongClick() {
  17. sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
  18. boolean handled = false;
  19. if (mOnLongClickListener != null) {
  20. // 回調用戶實現的長按操作監聽函數(OnLongClickListener)
  21. handled = mOnLongClickListener.onLongClick(View.this);
  22. }
  23. if (!handled) {
  24. // 如果OnLongClickListener的onLongClick返回false
  25. // 則需要繼續處理該長按事件,這裡是顯示上下文菜單
  26. handled = showContextMenu();
  27. }
  28. if (handled) {
  29. // 長按操作事件被處理了,此時應該給用戶觸覺上的反饋
  30. performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
  31. }
  32. return handled;
  33. }
Copyright © Linux教程網 All Rights Reserved