這是我我的的見解,要學好android觸控,瞭解MotionEvent是必要,對所用的MotionEvent經常使用的API要比較深刻的瞭解. java
下面是我我的的學習過程記錄: android
android.view.MotionEvent git
MotionEvent源代碼能夠在ocs看到,固然你也能夠在SDK中下載源代碼,或者其餘地方,如: github
通常咱們是在View的onTouchEvent方法中處理MotionEvent對象的. 學習
public boolean onTouchEvent(MotionEvent event) spa
在這裏咱們須要從一個MotionEvent對象中得到哪些信息呢? .net
能夠經過getAction(),在android2.2以後加入多點觸控支持以後使用getActionMasked()方法. 翻譯
這兩個方法的區別見後文. code
主要的事件類型有:
ACTION_DOWN: 表示用戶開始觸摸.
ACTION_MOVE: 表示用戶在移動(手指或者其餘)
ACTION_UP:表示用戶擡起了手指
ACTION_CANCEL:表示手勢被取消了,一些關於這個事件類型的討論見:http://stackoverflow.com/questions/11960861/what-causes-a-motionevent-action-cancel-in-android
還有一個不常見的:
ACTION_OUTSIDE: 表示用戶觸碰超出了正常的UI邊界.
可是對於多點觸控的支持,Android加入瞭如下一些事件類型.來處理,如另外有手指按下了,
有的手指擡起來了.等等:
ACTION_POINTER_DOWN:有一個非主要的手指按下了.
ACTION_POINTER_UP:一個非主要的手指擡起來了
getX() 得到事件發生時,觸摸的中間區域在屏幕的X軸.
getY() 得到事件發生時,觸摸的中間區域在屏幕的X軸.
在多點觸控中還能夠經過:
getX(int pointerIndex) ,來得到對應手指事件的發生位置. 得到Y軸用getY(int pointerIndex)
getEdgeFlags():當事件類型是ActionDown時能夠經過此方法得到,手指觸控開始的邊界. 若是是的話,有以下幾種值:EDGE_LEFT,EDGE_TOP,EDGE_RIGHT,EDGE_BOTTOM
(1)首先是MotionEvent 中getAction()與getActionMasked()的區別:
首先看代碼:
/** * Bit mask of the parts of the action code that are the action itself. */ public static final int ACTION_MASK = 0xff; /** * Return the kind of action being performed. * Consider using {@link #getActionMasked} and {@link #getActionIndex} to retrieve * the separate masked action and pointer index. * @return The action, such as {@link #ACTION_DOWN} or * the combination of {@link #ACTION_POINTER_DOWN} with a shifted pointer index. */ public final int getAction() { return mAction; } /** * Return the masked action being performed, without pointer index information. * Use {@link #getActionIndex} to return the index associated with pointer actions. * @return The action, such as {@link #ACTION_DOWN} or {@link #ACTION_POINTER_DOWN}. */ public final int getActionMasked() { return mAction & ACTION_MASK; }
上面的代碼是基於android2.2的,註釋是android4.X中最新的.
他們有什麼區別呢?若是mAction的值是在0x00到0xff之間的話。getAction()返回的值,和
getActionMasked()的返回的值是同樣的。
(Q1)那何時返回的值是同樣的呢?即當mAction值大於0xff時,那何時會大於0xff呢?
這就是是當有多點觸控時。當有多點觸控時。
mAction的低8位即0x00到0xff用來表示動做的類型信息。
例如:MotionEvent#ACTION_DOWN的值是 0,即0x00。
MotionEvent#ACTION_UP的值是 1,即0x01。
等等。
可是,咱們知道Android是支持多點觸控的,那麼怎麼知道這個一個MotionEvent是哪個
觸控點觸發的呢?那麼就還須要MotionEvent帶有觸控點索引信息。
Android的解決方案時在;mAction的第二個8位中存儲。
例如,若是mAction的值是0x0000,則表示是第一個觸控點的ACTION_DOWN操做。
若是mAction的值是0x0100呢,則表示是第二個觸控點的ACTION_DOWN操做。
第三個的ACTION_DOWN呢?相信你能夠推出來是0x0200。
總而言之,mAction時的低8位(也就是0-7位)是動做類型信息。
mAction的8-15位呢,是觸控點的索引信息。(即表示是哪個觸控點的事件)。
(Q2),爲何不用兩個字段來表示。
如 int mAction,int mPointer,
mAction表示動做類型,mPointer表示第幾個觸控點。
由於,動做類型只要0-255就能夠了,動做類型,mPointer也是。
只要一個字段(32位),不然須要兩個字段(32*2=64位),便可以節約內存。又能夠方便提升處理速度。
不過一般咱們都是以不一樣的字段來存儲不一樣的信息。可是在計算機內部他們仍是變成了0,1。
計算機始終仍是以位來存儲信息的。若是咱們多我熟悉以位爲基本單位來理解信息的存儲。對於理解android中的不少變量是頗有幫助的。由於他其中的不少東西使用的這樣的節約內在的技巧。
如onMeasure中的MeasureSpec。
=================== the above is update at 2013-01-24=====
先看關於這兩個方法註釋:
我簡單的翻譯以下:
/** * action碼的位掩碼部分就是action自己 */ public static final int ACTION_MASK = 0xff; /** 返回action的類型,考慮使用getActionMasked()和getActionIndex()來得到單獨的通過掩碼的action和觸控點的索引. @return action例如ACTION_DOWN或者ACTION_POINTER_DOWN與轉換的觸控點索引的合成值 */ public final int getAction() { return mAction; } /** 返回通過掩碼的action,沒有觸控點索引信息. 經過getActionIndex()來獲得觸控操做點的索引. @return action,例如ACTION_DOWN,ACTION_POINTER_DOWN */ public final int getActionMasked() { return mAction & ACTION_MASK; }
在上面的兩個方法中註釋出現差別的地方是對於ACTION_POINTER_DOWN的描述:
經過getAction()返回的ACTION_POINTER_DOWN的是與轉換觸控點索引的合成值.
而getActionMasked()則就是一個ACTION_POINTER_DOWN的值:
這麼來看咱們知道一個action的代碼值還包含了action是那個觸控點的索引值:
如今咱們對比來看看ACTION_MASK和ACTION_POINTER_INDEX_MASK
public static final int ACTION_MASK = 0xff; public static final int ACTION_POINTER_INDEX_MASK = 0xff00;尚未看出來什麼嗎?
您把ACTION_MASK當作是0x00ff
就知道了吧.
也就是說,一個MotionEvent中的action代碼,
前8位是實實在在包含表示哪個動做常量.
後八位呢就是包含了觸控點的索引信息.
由於ACTION_MASK = 0x00ff因此,通過ACTION_MASK掩碼事後的action碼就沒有索引信息了.
如何得索引值呢?
原理:
先將action跟0xff00相與清除前8位用於存儲動做常量的信息,
而後將action右移8位就能夠獲得索引值了.
咱們就能夠本身想辦法獲得索引信息了.
即先對action用ACTION_POINTER_INDEX_MASK進行掩碼處理,
即 maskedIndex = action&ACTION_POINTER_INDEX_MASK = action&0xff00
這各掩碼也就是將action這個數的前8位清零.
而後再將maskedIndex向右移8位就可以獲得索引值了.
再看看android真實是怎麼作的吧,
用於右移8位的常量.
/** * Bit shift for the action bits holding the pointer index as * defined by {@link #ACTION_POINTER_INDEX_MASK}. */ public static final int ACTION_POINTER_INDEX_SHIFT = 8;
再年獲得索引值方法源代碼,以下:
public final int getActionIndex() { return (mAction & ACTION_POINTER_INDEX_MASK) >> ACTION_POINTER_INDEX_SHIFT; }
爲何要有索引信息?
由於,這樣說吧,android中,當有觸摸事件發生時(假設已經註冊了事件監聽器),調用你註冊監聽器中的方法onTouch(,MotionEvent ev);傳遞了一個MotionEvent的對象過來.
可是,想一想,上面只傳遞進來一個MotionEvent過來,若是隻是單點觸控那是沒有問題.
問題就是當你多個手指觸控的時候也是隻傳遞這一個MotionEvent進來,
這個時候,你固然想知道每一個手指的所對應的觸控點數據信息啦.
因此MotionEvent中有就要索引信息了.
事件是你能夠很容易經過API看到,MotionEvent還包含了移動操做中其它歷史移動數據.
方便處理觸控的移動操做.
android sdk對於這個類的描述中就有這麼一句:
For efficiency, motion events with ACTION_MOVE may batch together multiple movement samples within a single object.
我翻譯下:"出於效率的考慮,事件代碼爲ACTION_MOVE的Motion,會在一個MotionEvent對象中包含多個移動數據採樣."
如今咱們對於MotionEvent有了初步的瞭解了.
PS:
我發現android4中MotionEvent中的代碼大多變成了原生代碼了:
例如如getX(int)在2.2中是這樣的:
public final float getX(int pointerIndex) { return mDataSamples[(pointerIndex*NUM_SAMPLE_DATA) + SAMPLE_X]; }但到了4.x是這樣的了:
public final float getX(int pointerIndex) { return nativeGetAxisValue(mNativePtr, AXIS_X, pointerIndex, HISTORY_CURRENT); }是否是進步了呢?哈哈!