Android View 的事件體系

東平人氏發表於2018-12-22

事件體系裡,除《Android 事件分發機制》這部分內容,是較深入研究了原始碼後總結整理出來的。

其餘內容基本都是閱讀《Android 開發藝術探索》(任玉剛 著)這本書後整理的筆記,內容估計和書上差別不大,已經閱讀過書籍的,可以不用閱讀了,有興趣的可以看看《事件分發機制》那篇部落格,那篇要比書上講解的要深入許多。

為了配合《事件分發機制》那篇部落格,所以這篇部落格沒寫完就發出來了,距離完整版可能還會延期 N 久,捂臉。

前言

文章重點內容為:事件分發機制、滑動衝突解決

目錄(相容稀土掘金)

  • 一、View 的位置引數
  • 二、MotionEvent 和 TouchSlop
  • 三、事件分發機制(另一篇部落格)
  • 三個重要方法
  • 事件分發機制簡略過程圖
  • 從 Activity 到 View 的事件傳遞過程(簡略)
  • View 的事件傳遞
  • ViewGroup 的事件傳遞
  • 從 Activity 到 View 的事件傳遞過程(完整)
  • 四、

一、View 的位置引數

位置引數都是相對父容器的座標。座標單位都是 pixel,可歸類為以下三類:

  • View 的原始左上角(left, top)、右下角(right, bottom)的座標決定 View 的位置
  • View 的左上角座標(x, y)
  • translationX、translationY 表示 View 的左上角相對父容器的偏移量

區別

View 在平移的過程中,top、left 表示原始左上角的座標,其值不會發生改變。此時該改變的是x、y、translationX、translationY Android 3.0 開始,才追加 x、y、translationX、translationY

它們的轉換規則為

x = left + translationX
y = top + translationY
複製程式碼

相關API

getTop()
getBottom()
getLeft()
getRight()
getX()
getY()
getTranslationX()
getTranslationY()
複製程式碼

二、MotionEvent 和 TouchSlop

  1. MotionEvent

表示手指觸控螢幕所產生的一系列事件。型別有:

final int ACTION_DOWN           = 0; // 典型事件
final int ACTION_UP             = 1; // 典型事件
final int ACTION_MOVE           = 2; // 典型事件
final int ACTION_CANCEL         = 3;
final int ACTION_OUTSIDE        = 4;
final int ACTION_POINTER_DOWN   = 5; // 貌似是多滑鼠操作
final int ACTION_POINTER_UP     = 6;
final int ACTION_HOVER_MOVE     = 7;
final int ACTION_SCROLL         = 8;
final int ACTION_HOVER_ENTER    = 9; // 滑鼠懸浮
final int ACTION_HOVER_EXIT     = 10;
final int ACTION_BUTTON_PRESS   = 11; // 按鈕按下
final int ACTION_BUTTON_RELEASE = 12; // 按鈕釋放
複製程式碼

相關API

// 獲取滑鼠座標:相對當前 View 的左上角的 x 和 y 座標
getX()
getY()

// 獲取滑鼠座標:獲取相對手機螢幕的左上角的 x 和 y 座標
getRawX()
getRawY()
複製程式碼
  1. TouchSlop:最小滑動距離

TouchSlop 是系統所能識別出的被認為是滑動的最小距離。即當手指在手機螢幕上的滑動距離小於該值時,那麼系統不認為你在進行滑動操作。預設最小值為 8dp。

定義檔案路徑:framewoks/base/core/res/res/values/config.xml

相關API

ViewConfiguration.get(context).getScaledTouchSlop();
複製程式碼

三、事件分發機制(另一篇部落格)

詳細內容見部落格《Android View 的事件體系 -- 事件分發機制》,連結見文章頭部

四、滑動衝突

同樣包含在部落格《Android View 的事件體系 -- 事件分發機制》中了

下面的內容基本可以不用看了,捂臉

VelocityTracker

GestureDetector

Scroller

五、View 的滑動

實現 View 的滑動有三種方法:

  • 通過 View 本身提供的 scrollTo()、scrollBy()
  • 通過動畫給 View 施加平移效果
  • 通過改變 View 的 位置引數或 LayoutParams 使得 View 重新佈局來實現滑動

1. scrollTo()、scrollBy() 實現滑動

這兩個方法都是將 View 內容進行移動,並不是將 View 本身進行移動。它們的具體實現如下:

/**
 * View 類
 */
/**
 * @param x 水平方向的偏移量,單位 pixel
 * @param y 垂直方向的偏移量,單位 pixel
 */
public void scrollTo(int x, int y) {
    // mScrollX: 表示 View 左邊緣與 View 內容左邊緣的水平方向的距離,單位 pixel
    // mScrollY: 表示 View 上邊緣與 View 內容上邊緣的垂直方向的距離,單位 pixel

    if (mScrollX != x || mScrollY != y) {
        int oldX = mScrollX;
        int oldY = mScrollY;
        mScrollX = x;
        mScrollY = y;
        invalidateParentCaches();
        onScrollChanged(mScrollX, mScrollY, oldX, oldY);
        if (!awakenScrollBars()) {
            postInvalidateOnAnimation();
        }
    }
}

public void scrollBy(int x, int y) {
    scrollTo(mScrollX + x, mScrollY + y);
}
複製程式碼

2. 平移動畫實現滑動

具體實現不多說。View 動畫是對 View 的影像做操作,並不會真正改變 View 的位置引數。如果希望改變 View 的位置引數,則可以通過屬性動畫實現。

3. 改變位置引數或 LayoutParams 實現滑動

改變的是 View 本身的位置引數或 LayoutParams。有兩種方法實現:

  • 修改 View 的 marginLeft 等引數實現
  • 在 View 的反向位置放置一個空白 View。通過修改空白 View 的寬高實現。

參考程式碼:

// 改變 LayoutParams 引數
MarginLayoutParams mlp = (MarginLayoutParams) button.getLayoutParams();

mlp.leftMargin += 100; // 方法一
mlp.width += 100; // 方法二

button.requestLayout(); // 或 button.setLayoutParams(mlp);

// 改變位置引數
button.setTranslationX(50); // 方法一
button.setTranslationY(50); // 方法一
複製程式碼

4. 三種方法對比

總結如下:

  • scrollTo()、scrollBy():操作簡單,適合對 View 內容的滑動
  • 動畫:操作簡單,適用於沒有互動的 View 和實現複雜動畫效果
  • 改變 LayoutParams:操作稍微複雜,適用於有互動的 View

六、彈性滑動

TODO 待補充

1. 使用 Scroller

2. 使用動畫

3. 使用延時策略

參考

  • 《Android 開發藝術探索》(任玉剛 著) 第三章 View 的事件體系
  • Android 21 原始碼(主要)

版本

  • 2017-12-17:未完成版本

宣告

限於作者水平有限,出錯難免,請積極拍磚! 歡迎任何形式的轉載,轉載請保留本文原文連結:juejin.im/post/5c176b…

結言

很多內容是未完成的,感到很尷尬,有時間再補充吧,捂臉。

相關文章