Android自定義View的多點觸控
阿新 • • 發佈:2019-02-13
在Android遊戲開發中,自定義View的多點觸控技術必不可少,本文主要簡單講解下Android中多點觸控技術的基礎知識。
所謂多點觸控技術,就是手機螢幕上支援同時處理多個觸控點的觸屏或移動事件。多點觸控的關鍵點有以下三點:(1)需要LCD和應用程式同時支援;(2)通過重寫View類中的onTouchEvent()方法來實現;(3)通過event.getActionMasked()來判斷觸控點的性質(主控點/輔控點)和動作型別(按下/移動/擡起).
對於單點觸控,MotionEvent的型別常量及含義如下:
MotionEvent.ACTION_DOWN://觸控點按下
MotionEvent .ACTION_MOVE://觸控點移動
MotionEvent.ACTION_UP://觸控點擡起
對於多點觸控,MotionEvent的型別常量及含義如下:
MotionEvent.ACTION_DOWN://(主控點)第一個觸控點按下
MotionEvent.ACTION_POINTER_DOWN://(輔控點)第一個之後的觸控點按下
MotionEvent.ACTION_MOVE://主、輔控點移動
MotionEvent.ACTION_UP://最後一個觸控點擡起
MotionEvent.ACTION_POINTER_UP://非最後一個觸控點擡起
值得注意的是,對於多點觸控,主/輔控點的按下或擡起的事件都是針對一個觸控點的,也就是說可以通過與運算和移位運算獲得本次觸控事件的觸點id:
int id = (event.getAction()&MotionEvent.ACTION_POINTER_ID_MASK) >>> MotionEvent.ACTION_POINTER_ID_SHIFT;
而主/輔控點的移動事件則更多的是針對所有觸控點的,即event中包含了本次取樣時獲得的所有觸控點移動後的座標,因此,在移動事件中,通常需要對所有的觸控點進行相應的邏輯處理。
以下是一個簡單的多點觸控例子。
(1)觸控點的描述類
簡單地,觸控點需要描述其座標和id,本例中再加上觸控點的繪製半徑和顏色。
public class BNPoint {
float x;//觸控點x座標
float y;//觸控點y座標
int id;//觸控點id
int[] color;//觸控點的繪製顏色
static final float RADIS = 80;//觸控點繪製半徑
public BNPoint(float x,float y,int[] color,int id){
this.x = x;
this.y = y;
this.color = color;
this.id = id;
}
public void setLocation(float x,float y){
this.x = x;
this.y = y;
}
public void drawSelf(Paint paint, Paint paint1, Canvas canvas){
paint.setARGB(180,color[1],color[2],color[3]);
canvas.drawCircle(x,y,RADIS,paint);//繪製最外層的圓環
paint.setARGB(150,color[1],color[2],color[3]);
canvas.drawCircle(x,y,RADIS-10,paint);//繪製中間的圓環
canvas.drawCircle(x,y,RADIS-18,paint1);
canvas.drawText(id+"",x,y-100,paint1);//繪製id
}
}
(2)繪圖類
繪圖類主要處理所有觸控點的動作事件,維護觸控點列表,並繪製所有觸控點。
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {
private MainActivity activity;
private Paint paint;//畫筆
private Paint paint1;
private ArrayList<BNPoint> pointArrayList = new ArrayList<>();//觸控點列表
public MySurfaceView(MainActivity activity){
super(activity);
this.activity = activity;
this.getHolder().addCallback(this);
paint = new Paint();
paint.setAntiAlias(true);
paint1 = new Paint();
paint1.setAntiAlias(true);
paint1.setTextSize(35);
}
public void onDraw(Canvas canvas){
canvas.drawColor(Color.BLACK);
paint.setStrokeWidth(10);
paint.setStyle(Paint.Style.STROKE);
paint1.setColor(Color.WHITE);//設定畫筆paint1的顏色
paint1.setStrokeWidth(5);//設定畫筆paint1的粗細度
paint1.setStyle(Paint.Style.STROKE);//設定畫筆paint1的風格
for (BNPoint point:pointArrayList){
point.drawSelf(paint,paint1,canvas);//繪製所有觸控點
}
}
public int[] getColor(){
int[] result = new int[4];
result[0] = (int)(Math.random()*255);
result[1] = (int)(Math.random()*255);
result[2] = (int)(Math.random()*255);
result[3] = (int)(Math.random()*255);
return result;
}
public void repaint(){
SurfaceHolder holder = this.getHolder();
Canvas canvas = holder.lockCanvas();
try {
synchronized (holder){
onDraw(canvas);
}
}catch (Exception e){
e.printStackTrace();
}finally {
if(canvas!=null){
holder.unlockCanvasAndPost(canvas);
}
}
}
public boolean onTouchEvent(MotionEvent event){
//int action = event.getAction();//單點觸控
//int actionMasked = event.getAction()&MotionEvent.ACTION_MASK;//Android 1.6 和 Android 2.1中
int actionMasked = event.getActionMasked();//獲得多點觸控檢測點
int id = (event.getAction()&MotionEvent.ACTION_POINTER_ID_MASK) >>> MotionEvent.ACTION_POINTER_ID_SHIFT;//無符號右移獲取觸控點id
switch (actionMasked){
case MotionEvent.ACTION_DOWN://第一個觸控點按下
pointArrayList.add(id,new BNPoint(event.getX(id), event.getY(id),getColor(),id));//以id索引大小排序插入
//順序對應ACTION_MOVE時event.getX(i)取出座標的順序
break;
case MotionEvent.ACTION_POINTER_DOWN://第一個之後的觸控點按下
pointArrayList.add(id,new BNPoint(event.getX(id),event.getY(id),getColor(),id));//觸控點按下時,將獲得(0 ~ 個數-1)中
//可用的最小的id號,即後續按下的點可以獲得
//先前已經擡起的觸控點的id
break;
case MotionEvent.ACTION_MOVE://主、輔點移動
for (int i = 0;i<pointArrayList.size();i++){
try {
//float x =event.getX(pointArrayList.get(i).id);//可能會下標越界,因為觸控點的id不一定等於其下標
float x = event.getX(i);
float y = event.getY(i);
pointArrayList.get(i).setLocation(x,y);
}catch (Exception e){
Log.d("MySurfaceView", "onTouchEvent: point.id=" + pointArrayList.get(i).id);
e.printStackTrace();
}
}
//pointArrayList.get(id).setLocation(event.getX(id),event.getY(id));//id=0,即如果沒有迴圈遍歷,則只會更新主控點
break;
case MotionEvent.ACTION_UP://最後一個點擡起
pointArrayList.clear();
break;
case MotionEvent.ACTION_POINTER_UP://非最後一個點擡起
pointArrayList.remove(id);//刪除一個觸控點後,該觸控點之後的點會向前移動,使得點的id不一定等於下標
break;
}
repaint();
return true;
}
public void surfaceCreated(SurfaceHolder holder){
}
public void surfaceChanged(SurfaceHolder holder,int arg1,int arg2,int arg3){
this.repaint();
}
public void surfaceDestroyed(SurfaceHolder holder){
}
}
(3)主活動類
主活動類主要是構建和載入檢視。
public class MainActivity extends AppCompatActivity {
static float screenHeight;
static float screenWidth;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_main);
ActionBar actionBar = getSupportActionBar();
if(actionBar!=null){
actionBar.hide();//隱藏標題欄
}
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);//全屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);//豎屏
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
screenHeight = metrics.heightPixels;//螢幕大小
screenWidth = metrics.widthPixels;
MySurfaceView mySurfaceView = new MySurfaceView(this);
setContentView(mySurfaceView);
}
}
最終執行效果圖如下: