Android輸入事件詳解

jamesehng發表於2019-01-22

輸入事件

在 Android 系統中,從使用者與應用的互動中截獲事件的方法不止一種。如考慮截獲使用者介面內的事件,則可從使用者與之互動的特定檢視物件中捕獲事件。 為此,View 類提供了多種方法。

在您將用於構建佈局的各種 View 類中,您可能會注意到幾種看起來適用於 UI 事件的公共回撥方法。 當該物件上發生相應的操作時,Android 框架會呼叫這些方法。 例如,在觸控一個檢視物件(例如“按鈕”)時,對該物件呼叫 onTouchEvent() 方法。不過,為了截獲此事件,您必須擴充套件 View 類並重寫該方法。 然而,為了處理此類事件而擴充套件每個檢視物件並不現實。 正因如此,View 類還包含一系列巢狀介面以及您可以更加輕鬆定義的回撥。 這些介面稱為事件偵聽器,是您捕獲使用者與 UI 之間互動的票證。

儘管您通常會使用事件偵聽器來偵聽使用者互動,但有時您確實需要擴充套件 View 類以構建自定義元件。也許,您想擴充套件 Button 類來豐富某些內容的樣式。 在這種情況下,您將能夠使用該類的事件處理程式為類定義預設事件行為。

事件偵聽器

事件偵聽器是 View 類中包含一個回撥方法的介面。 當使用者與 UI 專案之間的互動觸發已註冊此檢視的偵聽器時,Android 框架將呼叫這些方法。

各事件偵聽器介面包含的回撥方法如下:

onClick()

在 View.OnClickListener 中。 當使用者觸控專案(處於觸控模式下)時,或者使用導航鍵或軌跡球聚焦於專案,然後按適用的“Enter”鍵或按下軌跡球時,將呼叫此方法。

onLongClick()

在 View.OnLongClickListener 中。 當使用者觸控並按住專案(處於觸控模式下)時,或者使用導航鍵或軌跡球聚焦於專案,然後按住適用的“Enter”鍵或按住軌跡球(持續一秒鐘)時,將呼叫此方法。

onFocusChange()

在 View.OnFocusChangeListener 中。 當使用者使用導航鍵或軌跡球導航到或遠離專案時,將呼叫此方法。

onKey()

在 View.OnKeyListener 中。 當使用者聚焦於專案並按下或釋放裝置上的硬按鍵時,將呼叫此方法。

onTouch()

在 View.OnTouchListener 中。 當使用者執行可視為觸控事件的操作時,其中包括按下、釋放或螢幕上的任何移動手勢(在專案邊界內),將呼叫此方法。
onCreateContextMenu()
在 View.OnCreateContextMenuListener 中。 當(因持續“長按”而)生成上下文選單時,將呼叫此方法。請參見選單開發者指南中有關上下文選單的闡述。
這些方法是其相應介面的唯一成員。要定義其中一個方法並處理事件,請在 Activity 中實現巢狀介面或將其定義為匿名類。然後,將實現的例項傳遞給相應的 View.set…Listener() 方法。 (例如,呼叫 setOnClickListener() 並向其傳遞 OnClickListener 實現。)

以下示例顯示瞭如何為按鈕註冊點選偵聽器。

// Create an anonymous implementation of OnClickListener
private OnClickListener mCorkyListener = new OnClickListener() {
    public void onClick(View v) {
      // do something when the button is clicked
    }
};

protected void onCreate(Bundle savedValues) {
    ...
    // Capture our button from layout
    Button button = (Button)findViewById(R.id.corky);
    // Register the onClick listener with the implementation above
    button.setOnClickListener(mCorkyListener);
    ...
}
複製程式碼

您可能還會發現,將 OnClickListener 作為 Activity 的一部分來實現更為方便。這樣可以避免載入額外的類和分配物件。例如:

public class ExampleActivity extends Activity implements OnClickListener {
    protected void onCreate(Bundle savedValues) {
        ...
        Button button = (Button)findViewById(R.id.corky);
        button.setOnClickListener(this);
    }

    // Implement the OnClickListener callback
    public void onClick(View v) {
      // do something when the button is clicked
    }
    ...
}
複製程式碼

請注意,上述示例中的 onClick() 回撥沒有返回值,但是其他某些事件偵聽器方法必須返回布林值。具體原因取決於事件。

對於這幾個事件偵聽器,必須返回布林值的原因如下:

onLongClick():此方法返回一個布林值,表示您是否已處理完事件,以及是否應該將它繼續傳下去。 也就是說,返回 true 表示您已經處理事件且事件應就此停止;如果您尚未處理事件和/或事件應該繼續傳遞給其他任何點選偵聽器,則返回 false。
onKey():此方法返回一個布林值,表示您是否已處理完事件,以及是否應該將它繼續傳下去。 也就是說,返回 true 表示您已經處理事件且事件應就此停止;如果您尚未處理事件和/或事件應該繼續傳遞給其他任何按鍵偵聽器,則返回 false。
onTouch(): 此方法返回一個布林值,表示偵聽器是否處理完此事件。重要的是,此事件可以擁有多個分先後順序的操作。 因此,如果在收到關閉操作事件時返回 false,則表示您並未處理完此事件,而且對其後續操作也不感興趣。 因此,您無需執行事件內的任何其他操作,如手勢或最終操作事件。
請記住,硬按鍵事件總是傳遞給目前處於焦點的檢視物件。它們從檢視 層次結構的頂部開始分派,然後向下,直至到達合適的目的地。如果您的檢視物件(或檢視物件的子項)目前具有焦點,那麼您可以看到事件經由 dispatchKeyEvent() 方法的分派過程。除了通過檢視捕獲按鍵事件,您還可以使用 onKeyDown() 和 onKeyUp() 接收 Activity 內部的所有事件。

此外

,考慮應用的文字輸入時,請記住:許多裝置只有軟體輸入法。 此類方法無需基於按鍵;某些可能使用語音輸入、手寫等。儘管輸入法提供了類似鍵盤的介面,但它通常不會觸發 onKeyDown() 系列的事件。除非您要將應用限制為帶有硬鍵盤的裝置,否則,在設計 UI 時切勿要求必須通過特定按鍵進行控制。 特別是,當使用者按下返回鍵時,不要依賴這些方法驗證輸入;請改用 IME_ACTION_DONE 等操作讓輸入法知曉您的應用預計會作何反應,這樣,可以通過一種有意義的方式更改其 UI。不要推斷軟體輸入法應如何工作,只要相信它能夠為應用提供已設定格式的文字即可。

注:Android 會先呼叫事件處理程式,然後從類定義呼叫合適的預設處理程式。 因此,從這些事件偵聽器返回 true 會停止將事件傳播到其他事件偵聽器,還會阻止回撥檢視物件中的預設事件處理程式。 因此,在返回 true 時請確保您要終止事件。

事件處理程式

如果您從檢視構建自定義元件,則將能夠定義幾種用作預設事件處理程式的回撥方法。在有關自定義元件的文件中,您將瞭解某些用於事件處理的常見回撥,其中包括:

onKeyDown(int, KeyEvent):在發生新的按鍵事件時呼叫
onKeyUp(int, KeyEvent):在發生按鍵彈起事件時呼叫
onTrackballEvent(MotionEvent):在發生軌跡球運動事件時呼叫
onTouchEvent(MotionEvent):在發生觸控式螢幕運動事件時呼叫
onFocusChanged(boolean, int, Rect):在檢視獲得或失去焦點時呼叫
還有一些其他方法值得您注意,儘管它們並非 View 類的一部分,但可能會直接影響所能採取的事件處理方式。 因此,在管理佈局內更復雜的事件時,請考慮使用以下其他方法:

Activity.dispatchTouchEvent(MotionEvent):此方法允許 Activity 在分派給視窗之前截獲所有觸控事件。
ViewGroup.onInterceptTouchEvent(MotionEvent):此方法允許 ViewGroup 監視分派給子檢視的事件。
ViewParent.requestDisallowInterceptTouchEvent(boolean): 對父檢視呼叫此方法表明不應使用 onInterceptTouchEvent(MotionEvent) 截獲觸控事件。

觸控模式

當使用者使用方向鍵或軌跡球導航使用者介面時,必須聚焦到可操作專案上(如按鈕),以便使用者看到將接受輸入的物件。 但是,如果裝置具有觸控功能且使用者開始通過觸控介面與之互動,則不再需要突出顯示專案或聚焦到特定檢視物件上。 因此,有一種互動模式稱為“觸控模式”。

對於支援觸控功能的裝置,當使用者觸控螢幕時,裝置會立即進入觸控模式。 自此以後,只有 isFocusableInTouchMode() 為 true 的檢視才可聚焦,如文字編輯小部件。其他可觸控的檢視(如按鈕)在使用者觸控時不會獲得焦點;按下時它們只是觸發點選偵聽器。

無論何時,只要使用者點選方向鍵或滾動軌跡球,裝置就會退出觸控模式並找到一個檢視使其獲得焦點。 現在,使用者可在不觸控螢幕的情況下繼續與使用者介面互動。

整個系統(所有視窗和 Activity)都將保持觸控模式狀態。要查詢當前狀態,您可以呼叫isInTouchMode() 來檢查裝置目前是否處於觸控模式。

處理焦點

該框架將處理例行焦點移動來響應使用者輸入。其中包括在檢視被移除或隱藏時或在新檢視變得可用時更改焦點。 檢視物件表示願意通過 isFocusable() 方法獲得焦點。要設定檢視能否獲得焦點,請呼叫 setFocusable()。在觸控模式中,您可以使用 isFocusableInTouchMode() 查詢檢視是否允許聚焦,而且可以使用 setFocusableInTouchMode() 對此進行更改。

焦點移動所使用的演算法會查詢指定方向上距離最近的元素。 在極少數情況下,預設演算法可能與開發者的期望行為不一致。 在這些情況下,您可以在佈局檔案中顯式重寫以下 XML 屬性:nextFocusDown、nextFocusLeft、nextFocusRight 和 nextFocusUp。 將其中一個屬性新增到失去焦點的檢視。 將屬性的值定義為應該聚焦的檢視的 ID。例如:

<LinearLayout
    android:orientation="vertical"
    ... >
  <Button android:id="@+id/top"
          android:nextFocusUp="@+id/bottom"
          ... />
  <Button android:id="@+id/bottom"
          android:nextFocusDown="@+id/top"
          ... />
</LinearLayout>
複製程式碼

一般來說,在此垂直佈局中,從第一個按鈕向上導航或從第二個按鈕向下導航,焦點都不會移到任何其他位置。 現在,頂部按鈕已將底部按鈕定義為 nextFocusUp(反之亦然),因而導航焦點將自上而下和自下而上迴圈往復。

若要將一個檢視宣告為在 UI 中可聚焦(傳統上並非如此),請在佈局宣告中將 android:focusable XML 屬性新增到該檢視。將值設定為 true。 此外,您還可以使用 android:focusableInTouchMode 將檢視宣告為在觸控模式下可聚焦。

要請求要獲得焦點的特定檢視,請呼叫 requestFocus()。

要偵聽焦點事件(檢視獲得或失去焦點時會收到通知),請使用 onFocusChange(), 如上文的事件偵聽器部分中所述。

相關文章