在 Android 裝置上,您可以透過幾種方法攔截使用者與您應用程式互動的事件。 在考慮使用者介面中的事件時,方法是從使用者互動的特定 View 物件擷取事件。View 類別可提供這項功能。
在您用來設計版面配置的各種View 類別中,您可能會注意到,某些公開的回呼方法非常適合用於 UI 事件。Android 架構會在該物件發生相應操作時,呼叫這些方法。舉例來說,使用者輕觸檢視畫面 (例如按鈕) 時,系統會對該物件呼叫 onTouchEvent()
方法。不過,為了攔截這個情況,您必須擴充該類別並將方法覆寫。但是,擴充每個 View 物件來處理這類事件並不可行。這就是為什麼 View 類別也包含了一系列巢狀結構介面,以及您可更輕鬆地定義的回呼。這類介面稱為事件監聽器,讓您用來擷取使用者與使用者介面的互動。
雖然事件監聽器較常用於監聽使用者互動,但有時您的確會需要擴充 View 類別來建構自訂元件。您可能會想擴充 Button
類別來增添更豐富的功能。在這種情況下,您可以使用類別的事件處理常式,為類別定義預設的事件行為。
事件監聽器
事件監聽器是 View
類別中的一種介面,內含單一回呼方法。當使用者與使用者介面項目的互動觸發註冊了事件監聽器的 View 物件時,Android 架構就會呼叫這些方法。
事件監聽器介面包含以下回呼方法:
onClick()
- 來自
View.OnClickListener
。使用者輕觸項目 (觸控模式下) 或以瀏覽鍵或軌跡球將焦點移至該項目,以及按下合適的「Enter」鍵或按下軌跡球時,系統就會呼叫此方法。 onLongClick()
來自 - 。使用者輕觸並按住該項目(觸控模式下)或以瀏覽鍵或軌跡球將焦點移至該項目,以及長按(長達一秒)合適的「Enter」鍵或按下軌跡球時,系統就會呼叫此方法。
onFocusChange()
來自 - 。在使用者使用導覽鍵或軌跡球前往或離開某項目時,系統會呼叫此方法。
onKey()
來自 - 。當使用者將焦點移至該項目,並且按下或放開裝置上的硬體按鍵時,系統會呼叫此方法。
onTouch()
來自 - 。當使用者執行符合輕觸事件的操作時(包括按下、釋放,或是螢幕上任何在項目邊界內的手勢),系統就會呼叫此方法。
onCreateContextMenu()
來自 - 。在建立內容選單時(持續「長按」動作的結果),系統就會呼叫此方法。請參閱選單開發人員指南中有關內容選單的討論。
View.OnLongClickListener
的View.OnFocusChangeListener
的View.OnKeyListener
的View.OnTouchListener
的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()
- 這會傳回一個布林值,指出您是否已取用該事件,且不應繼續保留。 也就是說,傳回是表示已經處理該事件,應就此停止;傳回 否 表示尚未處理該事件及/或事件應繼續前往任何其他按鍵事件監聽器。onKey()
- 這會傳回一個布林值,表示事件監聽器是否取用該事件。重要的是,此事件之後可能依序會有多個操作。因此,如果在收到向下操作事件時傳回否,就表示尚未取用該事件,也對該事件的後續操作不感興趣。因此,系統不會向您呼叫事件中的任何其他操作,例如手指手勢或是最終的向上操作事件。onTouch()
請注意,系統一律會將硬體按鍵事件傳送至目前焦點所在的檢視畫面。從檢視區塊階層頂端開始傳送,然後再向下,直到到達適當的目的地為止。如果您的檢視畫面(或檢視畫面的子項)是目前的焦點,則可以透過
方法查看事件移動。除了透過檢視畫面擷取按鍵事件之外,您也可以透過 dispatchKeyEvent()
和 onKeyDown()
接收活動中的所有事件。onKeyUp()
此外,在考慮讓應用程式可輸入文字時,請記得許多裝置只有軟體輸入法。這類方法不必以按鍵為基礎;有些可能會使用語音輸入、手寫等。即使輸入方法呈現類似鍵盤的介面,通常也不會觸發
類型的事件。除非您想限制應用程式使用具有硬體鍵盤的裝置,否則請勿建構需要按壓特定按鍵進行控制的使用者介面。請特別注意,當使用者按下傳回鍵時,請勿仰賴這些方法驗證輸入;而是使用 onKeyDown()
IME_ACTION_DONE
等操作來向輸入方法指出應用程式的預期回應,使其可以透過合理的方式變更使用者介面。請避免假設軟體輸入方式的運作方式,只要信任它可為應用程式提供已格式化的文字即可。
注意:Android 會先呼叫事件處理常式,再從類別定義呼叫適當的預設處理常式。因此,從這些事件監聽器回傳是會停止將事件傳播至其他事件監聽器,也會一併封鎖檢視畫面中預設事件處理常式的回呼。因此,在傳回是時請確認您的確想要終止事件。
事件處理常式
如果您是從檢視畫面建構自訂元件,就可以定義幾個回呼方法做為預設事件處理常式。在自訂檢視畫面元件的文件中,您可以瞭解一些常用於事件處理的回呼,包括:
- 發生新的按鍵事件時呼叫。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()
為「是」的檢視畫面才會成為焦點,例如文字編輯小工具。其他輕觸式檢視畫面(例如按鈕),輕觸時不會聚焦;只會在按下時才會觸發其點擊事件監聽器。
使用者只要按下方向鍵或捲動軌跡球,裝置就會結束觸控模式,並找到可以聚焦的檢視畫面。現在,使用者無需觸碰螢幕,就能繼續與使用者介面互動。
觸控模式狀態是由整個系統(所有視窗及活動)所維持。如要查詢目前狀態,您可以呼叫 isInTouchMode()
來確認裝置目前是否處於觸控模式。
處理焦點
架構會根據使用者的輸入處理例行的焦點動作。包括檢視畫面移除或隱藏時,或是當有新的檢視畫面可用時,變更焦點。檢視畫面指示他們願意透過
方法聚焦。如要變更檢視畫面是否可以聚焦,請呼叫 isFocusable()
。在觸控模式下,您可以查詢檢視畫面是否允許以 setFocusable()
聚焦。如要變更此設定,請使用 isFocusableInTouchMode()
。setFocusableInTouchMode()
在搭載 Android 9(API 級別 28)及以上版本的裝置中,活動不會指派初始的焦點。相對地,如有需要,您必須明確地要求起始焦點。
聚焦動作是根據演算法尋找特定方向中最接近的位置。在極少數情況下,預設的演算法可能會與開發人員的預期行為不符。在這些情況下,您可以在版面配置檔案中使用下列 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(反之亦然),瀏覽焦點會從上至下及由下而上循環。
如果您想在使用者介面中宣告檢視畫面為可聚焦元素(傳統上並非如此),請在版面配置宣告中將 android:focusable
XML 屬性加至檢視畫面中。設定 true 的值。您也可以在輕觸模式時,使用 android:focusableInTouchMode
將檢視畫面宣告為可聚焦的元素。
如要要求特定檢視畫面接受聚焦,請呼叫
。requestFocus()
如要監聽聚焦事件(當檢視畫面收到或失去聚焦時通知),請使用如事件監聽器一節所述的
。onFocusChange()