1. 程式人生 > >Android自定義View的多點觸控

Android自定義View的多點觸控

在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);
    }
}
最終執行效果圖如下:

這裡寫圖片描述