在安卓中,有不止一種方法從你的應用截取用戶交互事件。在你的用戶界面中考慮事件,途徑就是從用戶界面中的一個指定的view對象中捕獲事件。該view提供了這樣做的方法。
在你用來組成你布局的不同的view類中,你或許注意到了一些公共的回調方法似乎看起來對UI事件有用。這些方法由安卓的框架調用,當各自的操作在對象中發生時。例如,當一個view (一個按鈕)被觸摸,在這個對象中的onTouchEvent() 方法就會被調用。然而,為了攔截這個事件,你必須繼承該類(button )並且重寫該方法(onTouchEvent)。然而,為了處理這樣的一個事件而繼承每一個view對象或許不實際。這就是為什么View 類經常包含一組嵌套的調用接口讓你可以跟方便的定義(事件處理方法)。這些接口,叫做事件監聽者(
event listeners),用來捕獲用戶與UI的交互。
當你更加平常的為你的用戶交互使用事件監聽者,當你想繼承一個view類,為了創建一個自定義組件,或許可以使用一次。或許你想要繼承
Button類來讓一些東西更加精美。在這樣的情況下,你或許需要為你的類定義一個默認事件行為,使用該類的事件處理者(
event handlers.).
一個事件監聽者是在view類中的一個接口,它包含了一個簡單的回調函數。這些方法將會被安卓框架調用,當該view的監聽者已經被注冊並且通過用戶ui 項被觸發。
包含了事件監聽者接口的都有如下回調方法:
onClick()
來自View.OnClickListener
.。當用戶觸摸該item(觸摸模式下),或者使用導航鍵或軌跡球使焦點聚集在該項上並且按下了適當的"enter"(進入)按鍵或者在軌跡球上執行了按下操作。
onLongClick()
來自
View.OnLongClickListener
.。當用戶持續觸摸item時(觸摸模式下),或者使用導航鍵或軌跡球使焦點聚集在該項上並且持續按着適當的"enter"(進入)按鍵或者在軌跡球上執行持續按下操作(持續一秒)。
onFocusChange()
來自
View.OnFocusChangeListener
當用戶使用導航鍵或者軌跡球導航到或者離開該item 時。
onKey()
來自View.OnKeyListener
. 當用戶聚焦在了項目上並且按下或松開設備上的按鍵時
onTouch()
來自View.OnTouchListener
.當用戶執行一個合格的觸摸操作,包括按下,釋放或者任何屏幕上的手勢動作(在項目的邊界內)
onCreateContextMenu()
來自View.OnCreateContextMenuListener
. 當上下文菜單開始構建時調用(如持續“長按”的結果)。查看在
Menus開發向導中關於上下文菜單的討論。
這些方法是它們各自接口的唯一方法。為定義其中的一個方法並且處理你的事件,在你的activity中實現嵌套的接口,或者作為一個匿名類定義它。然后,傳遞一個你的實例引用給各自的View.set...Listener()方法。 method (例如,調用
setOnClickListener()並且傳遞一個你實例化的
OnClickListener)
下面一個案例展示了如何為一個按鈕注冊一個on-click監聽者。
// 為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()
回調並沒有返回值,但是一些其它的事件處理方法必須返回一個布爾類型。原因取決於事件。這里有少數的一些原因:
- 該方法返回一個布爾類型來指明你是否消耗了該事件,並且它不應該進一步傳遞下去。也就是說,返回true表明你處理了該事件並且它應該在這停下來;返回false,如果你沒有處理它或者該事件應該繼續傳遞給其它on-click監聽者。onLongClick()
- 該方法返回一個布爾類型來表明你是否消耗了該事件並且不應該進一步傳遞下去。也就是說,返回true表明你處理了該事件並且它應該在這停下來;返回false,如果你沒有處理它或者該事件應該繼續傳遞給其它on-click監聽者。onKey()
- 該方法返回一個布爾類型來表明你是否消耗了該事件。重要的是該事件可以有多個符合觸發條件的操作。因此,當按下事件被接受到,如果你返回了false,表明你沒有處理該事件並且對這個事件的后續行動不感興趣。因此,你不會為每一個操作都調用該事件,如手勢動作,或者最后的抬手動作(up action event)。onTouch()
請記住,硬件按鈕事件總是傳遞給當前焦點視圖。從view的頂層開始向下分發,直到到達適當的地方。如果你的view(或者在view中的子view)獲得了焦點,之后你可以通過
dispatchKeyEvent()方法查看到事件的移動。作為一個備選方法來從你的view中捕獲按鍵事件,你還可以在你activity中的
onKeyDown()
和
onKeyUp()
.內收到各種事件。
並且,當你的應用輸入文本時,需要注意的是許多設備只有軟件的輸入方法。這些方法不需要按鍵支持,或使用聲音輸入,手寫,等等。即使一個輸入方法呈現了一個類似鍵盤的界面,它通常也不會觸發
onKeyDown()這類的事件。你永遠不應該創建UI來約束指定按鍵的按壓,除非你想要限制你應用使用設備的硬件盤。特別的,當用戶按下返回按鈕時,不要依賴於這些方法來確認輸入;改為使用像
IME_ACTION_DONE一樣的操作來表明該輸入方法並且讓你的應用程序做出怎樣的反應。這可能會以一種有意義的方式來改變它的UI。避免設想一個軟件輸入方法應該是如何工作的,並且只需要信任它來為你的應用支持已經格式化的文本。
注意:安卓將會首先調用事件處理者,之后調用類中定義的適當的默認處理者。同樣的,從這些事件監聽者中返回true將會停止事件傳播給其它事件監聽者,並且阻塞view中的默認事件處理的回調。因此,當你返回true時,確定你想要終止該事件。
事件處理者(Event Handlers)
如果你想自定義一個view組件,那么你可能會定義一些回調方法作為默認的事件處理者來使用。在
Custom Components, 這篇文章中,你將會看到一些被用作事件處理的公共回調函數,包括:
- 當一個新的按鍵事件發生時調用onKeyDown(int, KeyEvent)
- 當一個按鍵松開(key up )事件發生時調用onKeyUp(int, KeyEvent)
- 當一個軌跡球運動事件發生時onTrackballEvent(MotionEvent)
- 當一個屏幕觸摸事件發生時onTouchEvent(MotionEvent)
- 當該view獲得或失去焦點時onFocusChanged(boolean, int, Rect)
這還有其他的一些你需要注意的方法,它們不是view類的一部分,但是可以直接影響你能處理事件的方式。因此,當在你的布局中管理更多復合事件時,考慮這些方法:
-允許你的activity攔截所有的觸摸事件,在他們分發給window之前。Activity.dispatchTouchEvent(MotionEvent)
- 允許一個viewgroup 來觀察事件是否派遣給了子view。ViewGroup.onInterceptTouchEvent(MotionEvent)
-調用該方法取決於父視圖,表明它不應該通過ViewParent.requestDisallowInterceptTouchEvent(boolean)
.攔截觸摸事件。onInterceptTouchEvent(MotionEvent)
觸摸模式(Touch Mode)
當用戶使用方向鍵或軌跡球在用戶界面上移動時,給予活動項(如按鈕)焦點是必須的,這樣用戶可以看見什么輸入將會被接受。如果該設備有觸摸能力,並且用戶開始通過觸摸來與界面交互,那么並不需要高亮顯示項目,或者給指定view 一個焦點。因此,有一個交互模式叫做“觸摸模式”(當今的安卓手機主要是該模式,因它沒有硬件輸入設備)
對於一個可觸摸的設備,移動用戶觸摸了屏幕,設備將會進入觸摸模式。從此刻開始,只有當view因為
isFocusableInTouchMode()為true時才可被聚焦,如文本編輯組件。其它view都是可觸摸的,如按鈕,在觸摸時將不會獲得焦點;當按下時,他們僅僅的激活他們的on-click 監聽者。
任何時候,用戶點擊一個方向鍵或滑動軌跡球,設備將會退出觸摸模式並且找到一個view使其獲得焦點。現在,用戶可以繼續與用戶界面交互,不使用觸摸屏幕這種方式。
觸摸模式狀態一直維持在整個系統中(所有Windows和activitys ).為查詢當前狀態,你可以調用
isInTouchMode()
來查看設備當前是否處於觸摸模式。
處理焦點(Handling Focus )
框架將會處理常規響應用戶輸入時的的焦點移動。這包括當view移除或隱藏時改變焦點,或一個新的view變為可用。view通過
isFocusable()方法來表明他們獲得焦點的意願。改變view是否能獲得焦點,調用
setFocusable()。當處於觸摸模式,你或許需要通過
isFocusableInTouchMode()查詢一個view是否允許聚焦。通過使用
setFocusableInTouchMode()
你可以改變它。
焦點的移動是基於一個算法的,它順着一個給定的方向尋找最近的(view)。少數情況下,默認算法或許不符合開發者的要求。在這種情況下,你可以提供詳細的覆蓋,通過在布局文件中使用如下xml 屬性:
nextFocusDown,
nextFocusLeft,
nextFocusRight, 和
nextFocusUp。將其中的一個屬性添加到view上。該屬性的值是下一個需要獲取焦點的view的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設置一樣(反之亦然),focus 焦點將會上-下 下-上的循環移動。
如果你想要在你UI中什么一個view是可聚焦的(當它一般情況下不是時),為view添加
android:focusable
xml屬性,在你的布局文件中聲明。設置該值為true。你也可以聲明該view的可聚焦性,當在觸摸模式時通過
android:focusableInTouchMode
.
為請求一個特別的view獲得焦點,調用
requestFocus()
為監聽焦點事件(當一個view獲得或失去焦點時通知),使用
onFocusChange(),在上文
#事件監聽者(Event Listeners)中討論的。