以下均轉自Android遊戲編程入門經典,轉載請標明出處
觸控螢幕是擷取使用者輸入最重要的一種方式。直到Android2.0版本才引入多點觸摸。
先測試單點觸摸事件,它適用於所有Android版本。我們在視圖中註冊一個OnTouchListener介面,並把觸摸時間傳遞給這個介面實現。OnTouchListener介面只有一個方法:public abstract boolean onTouch(View v, MotionEvent event)
第一個參數是指派該觸摸事件的View,第二個參數是獲得觸摸事件的參數。
OnTouchListener可在任何View中實現中通過View.setOnTouchListener方法進行註冊。在MotionEvent被指派給View本身之前會先調用OnTouchListener方法。我們可在onTouch()方法的實現中返回true通知該View,我們已經處理事件。如果返回為false,那麼View將自己處理該事件。
MotionEvent執行個體包含如下3個我們關心的方法:
MotionEvent.getX()和MotionEvent.getY();這兩個方法報告觸摸事件相對於View的X和Y座標。座標原點位於該視圖左上方,X軸指向右邊,Y軸指向下。座標是以像素為單位。該方法返回浮點型資料,因此該座標具有亞像素精度。
MotionEvent.getAction():返回觸摸事件的類型。它是一個整型數,具有如下值之一:MotionEvent.ACTION_DOWN、
MotionEvent.ACTION_MOVE、MotionEvent.ACTION_CANCEL和MotionEvent.ACTION_UP。
顧名思義,當手指觸控螢幕幕時,將觸發MotionEvent.ACTION_DOWN事件。
當手指移動時,則觸發MotionEvent.ACTION_MOVE事件。只要手指沒有完全脫離螢幕,總可以獲得MotionEvent.ACTION_MOVE事件
當手指再次離開螢幕時,MotionEvent.ACTION_UP事件就會觸發。
MotionEvent.ACTION_CANCEL事件則稍微神秘。文檔說明當前手勢取消時會觸發該事件,我們還是把它假設為MotionEvent.ACTION_UP事件。
下面是測試代碼:
package org.example.ch04_android_basics;import android.app.Activity;import android.os.Bundle;import android.util.Log;import android.view.MotionEvent;import android.view.View;import android.view.View.OnTouchListener;import android.widget.TextView;public class SingleTouchTest extends Activity implements OnTouchListener{StringBuilder builder = new StringBuilder();TextView textView;@Overridepublic boolean onTouch(View v, MotionEvent event) {// TODO Auto-generated method stubbuilder.setLength(0);switch(event.getAction()){case MotionEvent.ACTION_DOWN:builder.append("down, ");break;case MotionEvent.ACTION_MOVE:builder.append("move, ");break;case MotionEvent.ACTION_CANCEL:builder.append("cancle, ");break;case MotionEvent.ACTION_UP:builder.append("up, ");break;}builder.append(event.getX());builder.append(", ");builder.append(event.getY());String text = builder.toString();Log.d("TouchTest", text);textView.setText(text);return true;}@Overrideprotected void onCreate(Bundle savedInstanceState) {// TODO Auto-generated method stubsuper.onCreate(savedInstanceState);textView = new TextView(this);textView.setText("Touch and drag (one finger only)!");textView.setOnTouchListener(this);setContentView(textView);}}
運行效果是顯示觸發事件以及相應的座標,
對於處理多點觸摸事件就複雜的多,Android2.2版本後對多點觸摸做了修改,添加了新的方法和常量,甚至重新命名了常量。這些改變可能會讓處理多點觸摸容易些。不過只支援Android2.2以後的版本,為了支援Android2.0--Android2.21版本,我們使用Android2.0的API。
當處理多點觸摸事件時,我們使用重載的方法,它們帶有一個所謂的指標索引,如event.getX(pointerIndex);
pointIndex是MotionEvent的內部數組中的一個索引,它包含特定手指觸控螢幕幕事件的座標值。而真正識別螢幕上的一根手指是指標ID。指標ID是一個任一數字,可以唯一標識觸控螢幕幕的一個指標的執行個體。有一個方法MotionEvent.getPointerIdentifier(int pointerIndex),它返回一個基於指標索引的指標ID。只要手指還觸摸在螢幕上,一個指標ID就與一根手指保持相同,而指標索引就不一定這樣了。先來看看是如何擷取指標索引:
int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT;
ACTION_POINTER_ID_MASK的常量的值是0xff00,因此低8位為0,高8位為15,用於儲存事件的指標索引。
整數的低8位可從event.getAction()方法返回得到,用於儲存事件類型的值。我們通過MotionEvent.ACTION_POINTER_ID_SHIFT來移位,該值為8,因此實際上是將第15位移到第8位,第7位移到第0位。注意我們是擷取pointerIndex而常量卻是XXX_POINTER_ID_XXX而不是XXX_POINTER_INDEX_XXX
擷取事件類型,我們只需屏蔽指標索引:
int action = event.getAction() & MotionEvent.ACTION_MASK;
這裡我們會遇到新的事件類型,
MotionEvent.ACTION_POINTER_DOWN:除了第一根手指外的任何手指觸控螢幕幕,都將發生該事件,而第一根手指仍然產生MotionEvent.ACTION_DOWN事件。
MotionEvent.ACTION_POINTER_UP:多根手指觸控螢幕幕而一根手指離開螢幕時,將發生該事件。最後一根手指離開螢幕將產生MotionEvent.ACTION_UP事件,而該手指不一定是第一個觸控螢幕幕的手指。為了檢查單個MotionEvent中包含幾個事件,可使用MotionEvent.getPointerCount()方法,它會告訴我們MotionEvent中包含多少根手指的座標。然後我們可通過MotionEvent.getX()、
MotionEvent.getY()和MotionEvent.getPointerId()方法來得到指標ID和從指標索引0到MotionEvent.getPointerCount() - 1的座標值。
測試代碼如下:
package org.example.ch04_android_basics;import android.app.Activity;import android.os.Bundle;import android.view.MotionEvent;import android.view.View;import android.view.View.OnTouchListener;import android.widget.TextView;public class MultiTouchTest extends Activity implements OnTouchListener {StringBuilder builder = new StringBuilder();TextView textView;float[] x = new float[10];float[] y = new float[10];boolean[] touched = new boolean[10];int[] id = new int[10];private void updateTextView(){builder.setLength(0);for(int i = 0; i < 10; i++){builder.append(touched[i]);builder.append(", ");builder.append(id[i]);builder.append(", ");builder.append(x[i]);builder.append(", ");builder.append(y[i]);builder.append("\n");}textView.setText(builder);}@Overrideprotected void onCreate(Bundle savedInstanceState) {// TODO Auto-generated method stubsuper.onCreate(savedInstanceState);textView = new TextView(this);textView.setText("Touch and drag (multiple fingers supported)!");textView.setOnTouchListener(this);setContentView(textView);for(int i = 0; i < 10; i++){id[i] = -1;}updateTextView();}@Overridepublic boolean onTouch(View v, MotionEvent event) {// TODO Auto-generated method stubint action = event.getAction() & MotionEvent.ACTION_MASK;int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >>MotionEvent.ACTION_POINTER_ID_SHIFT;int pointerCount = event.getPointerCount();for(int i = 0; i < 10; i++){if(i >= pointerCount){touched[i] = false;id[i] = -1;continue;}if(event.getAction() != MotionEvent.ACTION_MOVE && i != pointerIndex){/* If it's an up/down/cancel/out event, mask the id to see if we should process it for this touch point */continue;}int pointerId = event.getPointerId(i);switch(action){case MotionEvent.ACTION_DOWN:case MotionEvent.ACTION_POINTER_DOWN:touched[i] = true;id[i] = pointerId;x[i] = (int)event.getX(i);y[i] = (int)event.getY(i);break;case MotionEvent.ACTION_UP:case MotionEvent.ACTION_POINTER_UP:case MotionEvent.ACTION_OUTSIDE:case MotionEvent.ACTION_CANCEL:touched[i] = false;id[i] = -1;x[i] = (int)event.getX(i);y[i] = (int)event.getY(i);break;case MotionEvent.ACTION_MOVE:touched[i] = true;id[i] = pointerId;x[i] = (int)event.getX(i);y[i] = (int)event.getY(i);break;}}updateTextView();return true;}}
運行效果: