輸入事件總覽

試用 Compose
Jetpack Compose 是 Android 推薦的 UI 工具包。瞭解如何在 Compose 中使用觸控和輸入功能。

在 Android 裝置上,您可以透過幾種方法攔截使用者與您應用程式互動的事件。 在考慮使用者介面中的事件時,方法是從使用者互動的特定 View 物件擷取事件。View 類別可提供這項功能。

在您用來設計版面配置的各種 View 類別中,您可能會注意到,某些公開的回呼方法非常適合用於 UI 事件。Android 架構會在該物件發生相應操作時,呼叫這些方法。舉例來說,使用者輕觸檢視畫面 (例如按鈕) 時,系統會對該物件呼叫 onTouchEvent() 方法。不過,為了攔截這個情況,您必須擴充該類別並覆寫方法。但是,擴充每個 View 物件來處理這類事件並不可行。這就是為什麼 View 類別也包含了一系列巢狀結構介面,以及您可更輕鬆地定義的回呼。這類介面稱為事件監聽器,讓您用來擷取使用者與使用者介面的互動。

雖然事件監聽器較常用於監聽使用者互動,但有時您的確會需要擴充 View 類別來建構自訂元件。您可能會想擴充 Button 類別來增添更豐富的功能。在這種情況下,您可以使用類別的事件處理常式,為類別定義預設的事件行為。

事件監聽器

事件監聽器是 View 類別中的介面,其中包含單一回呼方法。當使用者與使用者介面項目的互動觸發註冊了事件監聽器的 View 物件時,Android 架構就會呼叫這些方法。

事件監聽器介麵包含以下回呼方法:

onClick()
來自 View.OnClickListener。使用者輕觸項目 (觸控模式時)、以瀏覽鍵或軌跡球將焦點移至該項目,以及按下合適的「Enter」鍵或按下軌跡球時,系統就會呼叫此方法。
onLongClick()
來自 View.OnLongClickListener。使用者按住項目 (觸控模式時),或以瀏覽鍵或軌跡球將焦點移至該項目,或是按住合適的「Enter」鍵或按住軌跡球 (一秒) 時,系統就會呼叫此方法。
onFocusChange()
來自 View.OnFocusChangeListener。在使用者使用導覽鍵或軌跡球前往或離開某項目時,系統會呼叫此方法。
onKey()
來自 View.OnKeyListener。當使用者將焦點移至該項目,然後按下或釋放裝置上的硬體按鍵時,系統就會呼叫此方法。
onTouch()
來自 View.OnTouchListener。當使用者執行符合觸控事件的操作時 (包括按下、釋放,或是螢幕上任何在項目邊界內的手勢),系統就會呼叫此方法。
onCreateContextMenu()
來自 View.OnCreateContextMenuListener。在建立內容選單時(持續「長按」動作的結果),系統就會呼叫此方法。請參閱選單開發人員指南中有關內容選單的討論。

這些方法是這些介面唯一的內部內容物。若要定義其中一種方法並處理事件,請在活動中實作該巢狀介面,或將其定義為匿名類別。然後將實作的執行個體傳送至對應的 View.set...Listener() 方法。(例如呼叫 setOnClickListener() 並將其傳遞到 OnClickListener 的實作中)。

以下範例說明如何為按鈕註冊一個點擊事件監聽器。

Kotlin

protected void onCreate(savedValues: Bundle) {
    ...
    val button: Button = findViewById(R.id.corky)
    // Register the onClick listener with the implementation above
    button.setOnClickListener { view ->
        // do something when the button is clicked
    }
    ...
}

Java

// Create an anonymous implementation of OnClickListener
private OnClickListener corkyListener = 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(corkyListener);
    ...
}

此外,將「OnClickListener」實作為活動的一部分會更方便。這可避免載入多餘的類別和配置多餘的物件。舉例來說:

Kotlin

class ExampleActivity : Activity(), OnClickListener {
  
    protected fun onCreate(savedValues: Bundle) {
        val button: Button = findViewById(R.id.corky)
        button.setOnClickListener(this)
    }

    // Implement the OnClickListener callback
    fun onClick(v: View) {
        // do something when the button is clicked
    }
}

Java

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() - 這會傳回一個布林值,指出您是否已取用該事件,因此不應繼續保留。 也就是說,傳回表示已經處理該事件,應就此停止;傳回 false 表示尚未處理該事件及/或事件應繼續前往任何其他點擊事件監聽器。
  • onKey() - 這會傳回一個布林值,指出您是否已取用該事件,因此不應繼續保留。 也就是說,傳回表示已經處理該事件,應就此停止;傳回 false 表示尚未處理該事件及/或事件應繼續前往任何其他按鍵事件監聽器。
  • onTouch() - 這會傳回一個布林值,表示事件監聽器是否取用該事件。重要的是,此事件之後可能依序會有多個動作。因此,如果在收到向下操作事件時傳回,就表示尚未取用該事件,也對該事件的後續操作不感興趣。因此,系統不會向您呼叫事件中的任何其他操作,例如手指手勢或是最終的向上操作事件。

請注意,系統一律會將硬體按鍵事件傳送至目前焦點所在的檢視畫面。從檢視區塊階層頂端開始傳送,然後再向下,直到到達適當的目的地為止。如果您的檢視畫面 (或檢視畫面的子項) 目前是焦點,則可以透過 dispatchKeyEvent() 方法查看事件移動。除了透過檢視畫面擷取按鍵事件之外,您也可以透過 onKeyDown()onKeyUp() 接收活動中的所有事件。

此外,在考慮讓應用程式可輸入文字時,請記得許多裝置只有軟體輸入法。這類方法不必以按鍵為基礎;有些可能會使用語音輸入、手寫等。即使輸入方法呈現類似鍵盤的介面,通常也不會觸發 onKeyDown() 類型的事件。除非您想限制應用程式使用具有硬體鍵盤的裝置,否則請勿建構需要按壓特定按鍵進行控制的使用者介面。請特別注意,當使用者按下傳回鍵時,請勿仰賴這些方法驗證輸入;而是使用 IME_ACTION_DONE 等動作來向輸入方法指出應用程式的預期回應,使其可能會以有意義的方式變更使用者介面。請避免假設軟體輸入法的運作方式,只要信任它可為應用程式提供已格式化的文字即可。

注意:Android 會先呼叫事件處理常式,再從類別定義呼叫適當的預設處理常式。因此,從這些事件監聽器回傳會停止將事件傳播至其他事件監聽器,也會封鎖檢視畫面中預設事件處理常式的回呼。因此,在傳回時請確認您的確想要終止事件。

事件處理常式

如果您是從檢視畫面建構自訂元件,就可以定義幾個回呼方法做為預設事件處理常式。在自訂檢視畫面元件的文件中,您可以瞭解一些常用於事件處理的回呼,包括:

您也應留意其他方法,這些方法並不屬於 View 類別,但會直接影響您處理事件的方式。因此,在版面配置中管理更複雜的事件時,請考慮以下其他方法:

觸控模式

使用者透過方向鍵或軌跡球瀏覽使用者介面時,務必將焦點放在可採取行動的項目 (例如按鈕),以便使用者查看可接受輸入的內容。但是,如果裝置具有觸控功能,且使用者輕觸介面開始與介面互動,就不再需要醒目顯示項目或將焦點移至特定檢視畫面。因此,有一種互動模式稱為「觸控模式」。

如果是觸控式裝置,只要使用者輕觸螢幕,裝置就會進入觸控模式。在這個模式下,只有 isFocusableInTouchMode() 為「是」的檢視畫面才會成為焦點,例如文字編輯小工具。其他輕觸式檢視畫面 (例如按鈕) 輕觸時不會聚焦;只會在按下時才會觸發其點擊事件監聽器。

使用者只要按下方向鍵或捲動軌跡球,裝置就會結束觸控模式,並找到可以聚焦的檢視畫面。現在,使用者無需觸碰螢幕,就能繼續與使用者介面互動。

觸控模式狀態是由整個系統(所有視窗及活動)所維持。如要查詢目前狀態,您可以呼叫 isInTouchMode() 來確認裝置目前是否處於觸控模式。

處理焦點

該架構將根據使用者輸入內容處理例行焦點動作。包括檢視畫面移除或隱藏時,或是當有新的檢視畫面可用時,變更焦點。檢視畫面指示他們願意透過 isFocusable() 方法聚焦。如要變更檢視畫面是否可以聚焦,請呼叫 setFocusable()。在觸控模式下,您可以查詢檢視畫面是否允許以 isFocusableInTouchMode() 聚焦。如要變更此設定,請使用 setFocusableInTouchMode()

在搭載 Android 9 (API 級別 28) 以上版本的裝置中,活動不會指派初始焦點。相對地,如有需要,您必須明確地要求起始焦點。

聚焦動作是根據演算法尋找特定方向中最接近的位置。在極少數情況下,預設演算法可能會與開發人員的預期行為不相符。在這些情況下,您可以在版面配置檔案中使用下列 XML 屬性加以明確覆寫:nextFocusDownnextFocusLeftnextFocusRightnextFocusUp。將其中一個屬性新增至將離開聚焦的檢視畫面中。將屬性值定義為應前往聚焦的檢視畫面 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 (反之亦然),瀏覽焦點會從上至下及由下而上循環。

如果您想在使用者介面中宣告檢視畫面為可聚焦元素 (傳統上並非如此),請在版面配置宣告中將 android:focusable XML 屬性新增至檢視畫面。設定 true 的值。您也可以在輕觸模式時,使用 android:focusableInTouchMode 將檢視畫面宣告為可聚焦的元素。

如要要求特定檢視畫面接受聚焦,請呼叫 requestFocus()

如要監聽聚焦事件 (當檢視畫面收到或失去聚焦時通知),請使用如事件監聽器一節所述的 onFocusChange()