在上篇文章中已經瞭解到界面Activity
的繪製徹底依賴其加載的視圖組件View
,不只如此,用戶的每次觸摸操做均可以在界面Activity
內接收並響應,也能夠直接傳遞給其中的某個視圖View
響應。那麼對於用戶的操做,應該如何響應,而同一個操做究竟是做用於界面,仍是界面中的某一個子視圖?針對用戶的操做對象所產生的交互方式不一樣,本文將分別展開介紹。html
說到界面交互,很容易想到用戶在設備屏幕上的觸摸操做。但是屏幕那麼大要怎麼肯定用戶觸摸的位置呢?Android系統定義了一套屏幕座標規則,該規則不只適用於當前的屏幕交互,在後文說起的動畫繪製及其餘屏幕相關操做等都一樣適用。該規則將屏幕的左上角做爲屏幕座標的原點,從左上角往右上角延伸的方向做爲屏幕座標的x軸,從左上角往左下角延伸的方向做爲屏幕座標的y軸。android
好比針對一款 1024x512 尺寸的TV設備,其左下角的屏幕座標值爲 (0, 512),右下角的屏幕座標值爲 (1024, 512),右上角的屏幕座標值爲 (1024, 0),左上角的屏幕座標值爲 (0, 0)。數組
對屏幕的觸摸位置有了衡量標準,是否是就能夠根據不一樣的位置作觸摸操做了呢?說到觸摸操做,也須要細化以後單獨處理。Android系統將用戶操做行爲,大體分爲三種:按下行爲,滑動行爲,擡起釋放行爲。這樣系統就能夠根據每個操做行爲作單獨的響應處理了。app
另外,用戶的操做對象,除了上文提到的硬件設備屏幕之外,還有硬件設備的按鍵(包括硬件按鍵和虛擬按鍵)。只不過對按鍵的操做行爲只有按下行爲和擡起釋放行爲兩種,並且按鍵的操做不須要用到屏幕座標相關內容。動畫
基於上文的介紹,能夠在界面Activity
中能夠分別重寫下邊三個方法對用戶的界面操做交互作出響應。google
boolean onTouchEvent(MotionEvent event)
在子視圖沒有處理的狀況下,用戶對硬件設備屏幕的每個操做,都會回調一次該方法。
其參數android.view.MotionEvent事件類的實例化對象event。
event.getAction()
方法能夠獲取當前事件行爲,包括MotionEvent.ACTION_DOWN
按下行爲、MotionEvent.ACTION_MOVE
滑動行爲、MotionEvent.ACTION_UP
擡起釋放行爲等。
event.getX()
方法獲取當前操做的屏幕座標x軸值。
同理event.getY()
方法獲取當前操做的屏幕座標y軸值。spa
boolean onKeyDown(int keyCode, KeyEvent event)
在子視圖沒有處理的狀況下,用戶對硬件設備按鍵的每一次按下行爲,都會回調一次該方法。
參數一int類型的keyCode指定按鍵類型,通常其值與參數二event.getKeyCode()
相等。
參數二android.view.KeyEvent類的實例化對象event。
event.getAction()
方法一樣能夠獲取當前事件行爲,只有KeyEvent.ACTION_DOWN
按下行爲和KeyEvent.ACTION_UP
擡起釋放行爲兩個行爲值。
event.getKeyCode()
方法能夠獲取觸發當前事件的按鍵類型,其值包括KeyEvent.KEYCODE_HOME
HOME鍵,KeyEvent.KEYCODE_POWER
電源鍵,KEYCODE_VOLUME_UP
音量增長鍵等。code
boolean onKeyUp(int keyCode, KeyEvent event)
在子視圖沒有處理的狀況下,用戶對硬件設備按鍵的每一次擡起釋放行爲,都會回調一次該方法。其兩個參數與上述onKeyDown()
中的兩個參數相似。htm
相對來講,界面內的視圖響應要繁瑣一些,而能實現的效果也更多樣化。當把視圖View
做爲用戶的操做對象時,仍然能夠重寫上述界面響應的三個方法,可是系統視圖每每也封裝了一層更加簡單粗暴的響應方法。對象
在視圖中重寫界面響應的三個方法後,若是返回的結果爲true,則上文界面響應中的三個方法將不會被回調。
爲何須要封裝一層響應方法呢?用戶對視圖的操做,每每就是點擊(短期內執行按下行爲和擡起釋放行爲),長按(在執行按下行爲後等待一段時間再執行擡起釋放行爲),拖拽(在執行按下行爲後執行一段滑動行爲以後再執行擡起釋放行爲)這些固定操做類型。若是每一個視圖都要細分用戶的操做行爲,就會有大量冗餘的操做類型判斷代碼,因此AndroidSDK定義了一系列接口分別對應用戶的操做類型。視圖若是須要響應某個操做,只須要設置其操做類型接口的實例化對象,並在該對象中實現相關方法便可。而這些接口主要有如下三個。
onClick(View view)
方法,在該方法內響應響應視圖View
被用戶點擊後的代碼邏輯。onLongClick(View view)
方法,在該方法內響應響應視圖View
被用戶長按後的代碼邏輯。onDrag(View v, DragEvent event)
方法,在該方法內響應視圖View
被用戶拖拽後的代碼邏輯。另外,不一樣的系統視圖也可能有單獨設置的響應方法,或者自定義視圖也會提供單獨的響應方法,例如列表視圖中的某一行數據被單獨點擊後如何響應,這些都要根據具體的視圖類查找並使用對應的響應方法,這裏再也不贅述。
在上文界面響應的三個方法中,關於他們被回調的時機,有個前提是子視圖沒有處理,即子視圖的界面響應方法返回結果爲false。這就涉及到Android系統的事件傳遞機制了。
咱們知道界面Activity
在建立以後會調用setContentView(int layoutId)
加載根視圖View
,而根視圖裏邊則能夠內嵌一層層的子視圖。那麼,若是用戶將手指觸摸到屏幕上,會觸發按下行爲,該行爲做爲事件首先傳遞到根視圖中,以後根視圖再將該事件傳遞給子視圖,子視圖再將該事件傳遞給子視圖的子視圖,這樣按照加載時的嵌套順序一層層傳遞事件,稱之爲事件分發。
直到該事件傳遞到最後一層子視圖,或者某一層視圖再也不繼續傳遞該事件,那麼該事件將在最後傳遞到的這層視圖中被首先處理。而每層視圖在收到傳遞進來的事件後,都有兩條路能夠選擇,要麼將該事件繼續傳遞給子視圖,要麼本身處理該事件,若是選擇第二條路再也不繼續傳遞子視圖而是本身處理該事件,稱之爲事件攔截。
一旦某層視圖處理了該事件,那麼其父層視圖將繼續處理該事件,以後是父層的父層視圖處理該事件,事件被這樣一層層處理,直到根視圖處理該事件結束,稱之爲事件處理。
在經歷了事件分發和事件處理以後,這樣的一個事件傳遞機制就算完成了。而上文提到的每個事件,都是如此。
上述過程在代碼中的實現,只須要針對事件分發、事件攔截和事件處理分別定義一個可重寫的方法便可。可以重寫該方法的位置主要是android.app.Acitivty
和android.view.View
中,因爲事件攔截只會發生在子視圖的傳遞過程當中,在界面中並不須要,因此事件攔截對應的方法只在android.view.GroupView
中重寫。
boolean dispatchTouchEvent (MotionEvent event)
boolean onInterceptTouchEvent(MotionEvent event)
dispatchTouchEvent()
。返回結果爲true時,表示攔截該事件,將會回調當前視圖的onTouchEvent()
.boolean onTouchEvent (MotionEvent event)
上文介紹了針對一個界面Activity
的交互響應,那麼兩個界面Activity
之間如何交互呢?這就用到在加載界面一文中啓動Activity
所使用的android.content.Intent
意圖類了。不一樣於用戶與界面的交互,界面間交互主要是變量數據的共享,因此經過Intent
支持的交互數據類型是有限的。
在啓動一個界面Activity
以前要先建立意圖對象,在該意圖對象調用putExtras(Bundle bundle)
方法,能夠將要發送的數據打包成android.os.Bundle類型的實例存入。
而該Bundle
對象能夠存儲的數據類型支持包括boolean
、char
、byte
、short
、int
、float
、double
、long
八種基本數據類型,String
類型和實現Parcelable
接口的任意類型,及其[]
數組或ArrayList
數組,和其餘一些不經常使用類型。這些數據都是以key-value鍵值對的形式保存在Bundle
對象中。對於要保存的不一樣數據類型,分別調用對應的putT(String key, T value)
系列方法便可以參數一key和參數二value的形式存入,一樣能夠調用對應的getT(String key)
系列方法取出指定參數一key對應的value數據,這裏的T泛指支持的不一樣數據類型。
另外也能夠在建立的意圖對象中直接調用putExtra(String key, T value)
系列方法,將要發送的數據直接以key-value鍵值對的形式存入,一樣也可使用getTExtra(String key)
系列方法取出指定參數一key對應的value數據,這裏的T一樣泛指Bundle
可支持的不一樣數據類型。
在打包全部的數據後,就能夠在當前界面Activity
中繼續調用startActivity(Intent intent)
系列方法啓動Intent
意圖參數中指定的另外一界面Activity
了。
這裏的startActivity(Intent)
方法是最簡單的啓動方法,另外還有startActivity(Intent, Bundle)
在啓動時將要發送的數據打包做爲參數二傳入。
或者startActivityForResult(Intent intent, int requestCode)
在啓動時傳入一個惟一值做爲參數二,以區分啓動不一樣界面的意圖,在啓動的界面Activity
返回後,系統會調用當前界面Activity
中的onActivityResult(int requestCode, int resultCode, Intent data)
方法,所以能夠重寫該方法。並根據參數一的惟一性對以前啓動的不一樣界面意圖作區分處理。參數二是根據啓動界面不一樣關閉狀態所返回的結果值,默認爲android.app.Activity.RESULT_CANCELED
,另外也能夠爲android.app.Activity.RESULT_FIRST_USER
和android.app.Activity.RESULT_OK
,其值須要在啓動界面返回時設置。參數三是從啓動界面返回的Intent
類型,主要使用其中的Bundle
打包數據類型對象,一樣其值能夠在啓動界面返回時設置。
做爲接收數據的啓動界面Activity
,在其綁定上下文環境以後,通常是在onCreate(Bundle savedInstanceState)
方法中,可使用getIntent()
方法獲取傳遞進來的Intent
意圖對象,獲取該對象以後天然就能夠經過getBExtras()
或一系列getTExtra(String key)
獲取到打包的數據,這樣在啓動界面中就可使用在啓動以前上一個界面Activtiy
中的變量數據了。
而當啓動界面Activity
在被用戶操做返回時,系統將回調該啓動界面的onBackPressed()
方法,以後將該Activity
從棧中移出並銷燬。因此能夠重寫onBackPressed()
方法,在該方法中調用setResult(int resultCode, Intent data)
設置上文提到的返回時參數。
或者在啓動界面Activity
代碼中也能夠主動調用finish()
方法,以關閉當前界面。所以在調用finish()
方法以前先調用setResult(int resultCode, Intent data)
設置返回參數便可。