android觸控,先了解MotionEvent(一)


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}
是不是進步了呢?哈哈!

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM