編輯:關於Android編程
我們已經了解了android觸摸事件傳遞機制,接著我們再來研究一下與觸摸事件傳遞相關的幾個比較重要的類,比如MotionEvent。我們今天就來詳細說明一下這個類的各方面用法。
final float offsetX = mScrollX - child.mLeft; final float offsetY = mScrollY - child.mTop; event.offsetLocation(offsetX, offsetY); handled = child.dispatchTouchEvent(event); event.offsetLocation(-offsetX, -offsetY);
這段代碼清晰展示了父視圖把事件分發給子視圖時,getX()和getY所獲得的相關坐標是如何改變的。當父視圖處理事件時,上述兩個函數獲得的相對坐標是相對於父視圖的,然後通過上邊這段代碼,調整了相對坐標的值,讓其變為相對於子視圖啦。
事件類型
涉及MotionEvent使用的代碼一般如下:
int action = MotionEventCompat.getActionMasked(event);
switch(action) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
break;
}
這裡就引入了關於MotionEvent的一個重要概念,事件類型。事件類型就是指MotionEvent對象所代表的動作。比如說,當你的一個手指在屏幕上滑動一下時,系統會產生一系列的觸摸事件對象,他們所代表的動作有所不同。有的事件代表你手指按下這個動作,有的事件代表你手指在屏幕上滑動,還有的事件代表你手指離開屏幕。這些事件的事件類型就分別為ACTION_DOWN,ACTION_MOVE,和ACTION_UP。上述這個動作所產生的一系列事件,被稱為一個事件流,它包括一個ACTION_DOWN事件,很多個ACTION_MOVE事件,和一個ACTION_UP事件。

當然,除了這三個類型外,還有很多不同的事件類型,比如ACTION_CANCEL。它代表當前的手勢被取消。要理解這個類型,就必須要了解ViewGroup分發事件的機制。一般來說,如果一個子視圖接收了父視圖分發給它的ACTION_DOWN事件,那麼與ACTION_DOWN事件相關的事件流就都要分發給這個子視圖,但是如果父視圖希望攔截其中的一些事件,不再繼續轉發事件給這個子視圖的話,那麼就需要給子視圖一個ACTION_CANCEL事件。
其他的類型會在接下來的博文中一一解釋。
細心的同學會發現,在上一節我描述用戶手指在屏幕上滑動的例子時,特地說明了手指的數量為一個。那麼當用戶兩個或者多個手指在屏幕上滑動時,系統又會產生怎樣的事件流呢?
為了可以表示多個觸摸點的動作,MotionEvent中引入了Pointer的概念,一個pointer就代表一個觸摸點,每個pointer都有自己的事件類型,也有自己的橫軸坐標值。一個MotionEvent對象中可能會存儲多個pointer的相關信息,每個pointer都會有一個自己的id和index。pointer的id在整個事件流中是不會發生變化的,但是index會發生變化。
MotionEvent類中的很多方法都是可以傳入一個int值作為參數的,其實傳入的就是pointer的index值。比如getX(pointerIndex)和getY(pointerIndex),此時,它們返回的就是index所代表的觸摸點相關事件坐標值。
由於pointer的index值在不同的MotionEvent對象中會發生變化,但是id值卻不會變化。所以,當我們要記錄一個觸摸點的事件流時,就只需要保存其id,然後使用findPointerIndex(int)來獲得其index值,然後再獲得其他信息。
private final static int INVALID_ID = -1;
private int mActivePointerId = INVALID_ID;
private int mSecondaryPointerId = INVALID_ID;
private float mPrimaryLastX = -1;
private float mPrimaryLastY = -1;
private float mSecondaryLastX = -1;
private float mSecondaryLastY = -1;
public boolean onTouchEvent(MotionEvent event) {
int action = MotionEventCompat.getActionMasked(event);
switch (action) {
case MotionEvent.ACTION_DOWN:
int index = event.getActionIndex();
mActivePointerId = event.getPointerId(index);
mPrimaryLastX = MotionEventCompat.getX(event,index);
mPrimaryLastY = MotionEventCompat.getY(event,index);
break;
case MotionEvent.ACTION_POINTER_DOWN:
index = event.getActionIndex();
mSecondaryPointerId = event.getPointerId(index);
mSecondaryLastX = event.getX(index);
mSecondaryLastY = event.getY(index);
break;
case MotionEvent.ACTION_MOVE:
index = event.findPointerIndex(mActivePointerId);
int secondaryIndex = MotionEventCompat.findPointerIndex(event,mSecondaryPointerId);
final float x = MotionEventCompat.getX(event,index);
final float y = MotionEventCompat.getY(event,index);
final float secondX = MotionEventCompat.getX(event,secondaryIndex);
final float secondY = MotionEventCompat.getY(event,secondaryIndex);
break;
case MotionEvent.ACTION_POINTER_UP:
xxxxxx(涉及pointer id的轉換,之後的文章會講解)
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mActivePointerId = INVALID_ID;
mPrimaryLastX =-1;
mPrimaryLastY = -1;
break;
}
return true;
}
除了pointer的概念,MotionEvent還引入了兩個事件類型:
ACTION_POINTER_DOWN:代表用戶又使用一個手指觸摸到屏幕上,也就是說,在已經有一個觸摸點的情況下,有新出現了一個觸摸點。ACTION_POINTER_UP:代表用戶的一個手指離開了觸摸屏,但是還有其他手指還在觸摸屏上。也就是說,在多個觸摸點存在的情況下,其中一個觸摸點消失了。它與ACTION_UP的區別就是,它是在多個觸摸點中的一個觸摸點消失時(此時,還有觸摸點存在,也就是說用戶還有手指觸摸屏幕)產生,而ACTION_UP可以說是最後一個觸摸點消失時產生。 那麼,用戶先兩個手指先後接觸屏幕,同時滑動,然後在先後離開這一套動作所產生的事件流是什麼樣的呢?
它所產生的事件流如下:
ACTION_DOWN事件,代表用戶的第一個手指接觸到了屏幕。ACTION_POINTER_DOWN事件,代表用戶的第二個手指接觸到了屏幕。ACTION_MOVE事件,但是在這些MotionEvent對象中,都保存著兩個觸摸點滑動的信息,相關的代碼我們會在文章的最後進行演示。ACTION_POINTER_UP事件,代表用戶的一個手指離開了屏幕。ACTION_MOVE事件。ACTION_UP事件,代表用戶的最後一個手指離開了屏幕
看到文章開頭那段代碼的同學可能會有點疑問:好像在很多代碼裡,大家都是通過getAction獲得事件類型的,那麼它和getActionMasked又有什麼不同呢?
從上一節我們可以得知,一個MotionEvent對象中可以包含多個觸摸點的事件。當MotionEvent對象只包含一個觸摸點的事件時,上邊兩個函數的結果是相同的,但是當包含多個觸摸點時,二者的結果就不同啦。
getAction獲得的int值是由pointer的index值和事件類型值組合而成的,而getActionWithMasked則只返回事件的類型值
舉個例子(注:假設了int中不同位所代表的含義,可能不是例子所中的前8位代表id,後8位代表事件類型):
getAction() returns 0x0105. getActionMasked() will return 0x0005 其中0x0100就是pointer的index值。
一般來說,getAction() & ACTION_POINTER_INDEX_MASK就獲得了pointer的id,等同於getActionIndex函數;getAction()& ACTION_MASK就獲得了pointer的事件類型,等同於getActionMasked函數。
為了效率,Android系統在處理ACTION_MOVE事件時會將連續的幾個多觸點移動事件打包到一個MotionEvent對象中。我們可以通過getX(int)和getY(int)來獲得最近發生的一個觸摸點事件的坐標,然後使用getHistorical(int,int)和getHistorical(int,int)來獲得時間稍早的觸點事件的坐標,二者是發生時間先後的關系。所以,我們應該先處理通過getHistoricalXX相關函數獲得的事件信息,然後在處理當前的事件信息。
下邊就是Android Guide中相關的例子:
void printSamples(MotionEvent ev) {
final int historySize = ev.getHistorySize();
final int pointerCount = ev.getPointerCount();
for (int h = 0; h < historySize; h++) {
System.out.printf("At time %d:", ev.getHistoricalEventTime(h));
for (int p = 0; p < pointerCount; p++) {
System.out.printf(" pointer %d: (%f,%f)",
ev.getPointerId(p), ev.getHistoricalX(p, h), ev.getHistoricalY(p, h));
}
}
System.out.printf("At time %d:", ev.getEventTime());
for (int p = 0; p < pointerCount; p++) {
System.out.printf(" pointer %d: (%f,%f)",
ev.getPointerId(p), ev.getX(p), ev.getY(p));
}
}
之後的博文會繼續分析關於觸摸處理的幾個比較重要的類,比如OverScroller和EdgeEffect;然後會是一篇關於滑動手勢處理代碼分析的文章。請大家繼續關注。
Android項目之天氣預報 的實現分析
輸入要查詢的城市名稱,點擊查詢按鈕後,依次出現七天的天氣情況。出現時有動畫效果二、實現過程(一)獲取天氣預報數據1、首先搞定天氣預報數據來源的問題,提高天氣預報服務的有很
教你如何查看微博誰取消關注自己 新浪微博查看取消關注粉絲使用
近日微博上線了一個新功能“我的粉絲數據統計”,你不僅可以通過它查看到新增的粉絲,還可以通過它看到誰取關了自己,那麼微博查取消關注粉絲
漫談android系統(7)-log系統1
前言羅升陽的《Android系統源代碼情景分析》一書,有關log是如何顯示,那麼真的在代碼中是如何實現的呢?就該問題我想需要細細分析bootloader層的log在fir
Android 中的消息傳遞,詳解廣播機制
--------------------------------------廣播機制簡介-----------------------------------------