http://my.oschina.net/banxi/blog/56421
這是我個人的看法,要學好android觸控,了解MotionEvent是必要,對所用的MotionEvent常用的API要比較深入的了解.
下面是我個人的學習過程記錄:
android.view.MotionEvent
MotionEvent源代碼可以在ocs看到,當然你也可以在SDK中下載源代碼,或者其他地方,如:
https://github.com/android/platform_frameworks_base/blob/master/core/java/android/view/MotionEvent.java
MotionEvent事件對象
一般我們是在View的onTouchEvent方法中處理MotionEvent對象的.
public boolean onTouchEvent(MotionEvent event)
在這里我們需要從一個MotionEvent對象中獲得哪些信息呢?
(1)首先應該是事件的類型吧?
可以通過getAction(),在android2.2之后加入多點觸控支持之后使用getActionMasked()方法.
這兩個方法的區別見后文.
主要的事件類型有:
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:一個非主要的手指抬起來了
(2)事件發生的位置,x,y軸
getX() 獲得事件發生時,觸摸的中間區域在屏幕的X軸.
getY() 獲得事件發生時,觸摸的中間區域在屏幕的X軸.
在多點觸控中還可以通過:
getX(int pointerIndex) ,來獲得對應手指事件的發生位置. 獲得Y軸用getY(int pointerIndex)
(3)其他屬性
getEdgeFlags():當事件類型是ActionDown時可以通過此方法獲得,手指觸控開始的邊界. 如果是的話,有如下幾種值:EDGE_LEFT,EDGE_TOP,EDGE_RIGHT,EDGE_BOTTOM
一些討論
(1)首先是MotionEvent 中getAction()與getActionMasked()的區別:
首先看代碼:
01/**
02* Bit mask of the parts of the action code that are the action itself.
03*/
04public static final int ACTION_MASK = 0xff;
05
06/**
07* Return the kind of action being performed.
08* Consider using {<a href="http://my.oschina.net/link1212" target="_blank" rel="nofollow">@link</a> #getActionMasked} and {<a href="http://my.oschina.net/link1212" target="_blank" rel="nofollow">@link</a> #getActionIndex} to retrieve
09* the separate masked action and pointer index.
10* @return The action, such as {<a href="http://my.oschina.net/link1212" target="_blank" rel="nofollow">@link</a> #ACTION_DOWN} or
11* the combination of {<a href="http://my.oschina.net/link1212" target="_blank" rel="nofollow">@link</a> #ACTION_POINTER_DOWN} with a shifted pointer index.
12*/
13public final int getAction() {
14return mAction;
15}
16
17/**
18* Return the masked action being performed, without pointer index information.
19* Use {<a href="http://my.oschina.net/link1212" target="_blank" rel="nofollow">@link</a> #getActionIndex} to return the index associated with pointer actions.
20* @return The action, such as {<a href="http://my.oschina.net/link1212" target="_blank" rel="nofollow">@link</a> #ACTION_DOWN} or {<a href="http://my.oschina.net/link1212" target="_blank" rel="nofollow">@link</a> #ACTION_POINTER_DOWN}.
21*/
22public final int getActionMasked() {
23return mAction & ACTION_MASK;
24}
上面的代碼是基於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=====
先看關於這兩個方法注釋:
我簡單的翻譯如下:
01/**
02* action碼的位掩碼部分就是action本身
03*/
04public static final int ACTION_MASK = 0xff;
05
06/**
07返回action的類型,考慮使用getActionMasked()和getActionIndex()來獲得單獨的經過掩碼的action和觸控點的索引.
08@return action例如ACTION_DOWN或者ACTION_POINTER_DOWN與轉換的觸控點索引的合成值
09*/
10public final int getAction() {
11return mAction;
12}
13
14/**
15返回經過掩碼的action,沒有觸控點索引信息.
16通過getActionIndex()來得到觸控操作點的索引.
17@return action,例如ACTION_DOWN,ACTION_POINTER_DOWN
18
19
20*/
21public final int getActionMasked() {
22return mAction & ACTION_MASK;
23}
在上面的兩個方法中注釋出現差異的地方是對於ACTION_POINTER_DOWN的描述:
通過getAction()返回的ACTION_POINTER_DOWN的是與轉換觸控點索引的合成值.
而getActionMasked()則就是一個ACTION_POINTER_DOWN的值:
這么來看我們知道一個action的代碼值還包含了action是那個觸控點的索引值:
現在我們對比來看看ACTION_MASK和ACTION_POINTER_INDEX_MASK
1
public static final int ACTION_MASK = 0xff;
2
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位的常量.
1/**
2* Bit shift for the action bits holding the pointer index as
3* defined by {<a href="http://my.oschina.net/link1212" target="_blank" rel="nofollow">@link</a> #ACTION_POINTER_INDEX_MASK}.
4*/
5public static final int ACTION_POINTER_INDEX_SHIFT = 8;
再年得到索引值方法源代碼,如下:
1public final int getActionIndex() {
2return (mAction & ACTION_POINTER_INDEX_MASK) >> ACTION_POINTER_INDEX_SHIFT;
3}
為什么要有索引信息?
因為,這樣說吧,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中是這樣的:
1public final float getX(int pointerIndex) {
2return mDataSamples[(pointerIndex*NUM_SAMPLE_DATA) + SAMPLE_X];
3}
但到了4.x是這樣的了:
view sourceprint?
1public final float getX(int pointerIndex) {
2return nativeGetAxisValue(mNativePtr, AXIS_X, pointerIndex, HISTORY_CURRENT);
3}
是不是進步了呢?哈哈!
