Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android自定義SurfaceView與傳感器的並用(實現自繪的指北針)

Android自定義SurfaceView與傳感器的並用(實現自繪的指北針)

編輯:關於Android編程

概述:

SurfaceView是Android中極為重要的繪圖容器,SurfaceView的圖像繪制是放在主線程之外的另一個線程中完成的。除了繪圖,SurfaceView還能播放視頻。

實現方法:

實現Android的自定義SurfaceView,需要新建一個繼承於SurfaceView的類,並且重寫至少一種構造器,在構造器中,需要同過getHolder()方法得到一個SurfaceViewHolder類的對象holder,canvas畫布通過holder調用lockCanvas()方法鎖定並得到,用完畫布後需要holder調用unlockCanvasAndPost(canvas)方法解鎖畫布。在構造方法中,holder還必須通過addCallback()方法實現監聽器,監聽器中需要實現的方法有三個:surfaceCreated()、surfaceChanged()、surfaceDestroyed()。自定義SurfaceView的圖像需要在surfaceCreated()方法中繪制。

Demo

用SurfaceView繪制一個指北針的模型,並結合加速度傳感器和地磁傳感器讓它動起來

/**
 * 自定義SurfaceView繪制一個指南針
 * 繼承於SurfaceView,並實現它的構造器
 * 實現SurfaceHolder.Callback接口,實現surfaceCreated、surfaceChanged、surfaceDestroyed方法
 */
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback{

    private SurfaceHolder holder;
    private Canvas canvas;
    private Path path;

    private Paint mPaintCircle;
    private Paint mPaintLine;
    private Paint mPaintText;

    private int width;
    private int height;
    private float degree;
    private boolean isDrawing = true;

    private SensorManager sensorManager;
    private float lastDegree;

    public float getDegree() {
        return degree;
    }

    public void setDegree(float degree) {
        this.degree = degree;
    }

    public MySurfaceView(Context context) {
        super(context);
    }

    public MySurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);

        width = 1100;
        height = 1500;
        //首先得到一個SurfaceHolder對象
        holder = getHolder();
        //接著讓holder添加Callback監聽器
        holder.addCallback(this);

        //path用於畫指針
        path = new Path();
        //畫外環的畫筆
        mPaintCircle = new Paint();
        mPaintCircle.setColor(Color.BLACK);
        mPaintCircle.setStyle(Paint.Style.FILL_AND_STROKE);
        mPaintCircle.setStrokeWidth(30);
        //畫刻度線的畫筆
        mPaintLine = new Paint();
        mPaintLine.setColor(Color.WHITE);
        mPaintLine.setStyle(Paint.Style.FILL);
        mPaintLine.setStrokeWidth(10);
        //畫文本的畫筆
        mPaintText = new Paint();
        mPaintText.setTextSize(70);
        mPaintLine.setTextAlign(Paint.Align.LEFT);
        mPaintText.setColor(Color.WHITE);

        //sensorManager用於管理傳感器
        sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
        //地磁傳感器
        Sensor magneticSensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
        //加速度傳感器
        Sensor accelerometerSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        //分別給兩種傳感器注冊監聽器
        sensorManager.registerListener(listener, magneticSensor, SensorManager.SENSOR_DELAY_GAME);
        sensorManager.registerListener(listener, accelerometerSensor, SensorManager.SENSOR_DELAY_GAME);

    }

    @Override
    public void surfaceCreated(final SurfaceHolder holder) {
        /**
         * 在一個線程中進行繪制
         */
        new Thread(new Runnable() {
            @Override
            public void run() {
                //用一個無限循環,便於隨時改變圖形
                while(isDrawing) {
                    canvas = holder.lockCanvas();
                    canvas.drawColor(Color.BLUE);
                    canvas.drawCircle(width / 2, height / 2, 400, mPaintCircle);
                    for (int i = 0; i <= 35; i++) {
                        canvas.save();
                        canvas.rotate(10*i+degree,width / 2, height / 2);
                        canvas.drawLine(width / 2, height / 2 - 400, width / 2, height / 2 - 400 + 50, mPaintLine);
                        if (i == 0) {
                            canvas.drawText(N, width / 2, height / 2 - 400 - 40, mPaintText);
                        }else if(i == 8){
                            canvas.drawText(E , width / 2, height / 2 -400 -40, mPaintText);
                        }else if(i == 17){
                            canvas.drawText(S , width / 2, height / 2 -400 -40, mPaintText);
                        }else if(i == 26){
                            canvas.drawText(W , width / 2, height / 2 -400 -40, mPaintText);
                        }
                        canvas.restore();
                    }
                    //每當degree發生改變,canvas畫布都會轉動相應的角度
                    canvas.rotate(degree,width / 2, height / 2);

                    //繪制指針
                    path.moveTo(width / 2 - 10, height / 2);
                    path.lineTo(width / 2, height / 2 - 150);
                    path.lineTo(width / 2 + 10, height / 2);
                    path.close();
                    canvas.drawPath(path, mPaintLine);
                    //每次用完canvas,都要調用unlockCanvasAndPost()解鎖一次
                    holder.unlockCanvasAndPost(canvas);
                }
            }
        }).start();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    }

    /**注銷
     * 當調用surfaceDestroyed方法時,停止線程中的死循環,並且sensorManager的監聽器
     */
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        isDrawing = false;
        if(sensorManager!=null){
            sensorManager.unregisterListener(listener);
        }
    }
    //監聽傳感器
    private SensorEventListener listener = new SensorEventListener() {
        float[] accelerometerValues = new float[3];
        float[] magneticValues = new float[3];
        @Override
        public void onSensorChanged(SensorEvent event) {
            if(event.sensor.getType()==Sensor.TYPE_ACCELEROMETER){
                accelerometerValues = event.values.clone();
            }else if(event.sensor.getType()==Sensor.TYPE_MAGNETIC_FIELD){
                magneticValues = event.values.clone();
            }
            float[] R = new float[9];
            float[] values = new float[3];
            //得到包含旋轉矩陣的R數組
            SensorManager.getRotationMatrix(R, null, accelerometerValues, magneticValues);
            //計算手機的旋轉數據,並將參數存入values數組
            SensorManager.getOrientation(R, values);
            //將弧度轉換為角度
            degree = -(float) Math.toDegrees(values[0]);
            if(Math.abs(degree-lastDegree)>10){
                lastDegree = degree;
            }
        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) {

        }
    };

}

在xml布局中定義這個自定義SurfaceView




    

主活動:

public class SouthArrowActivity extends Activity {
    private MySurfaceView mySurfaceView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_source);

        mySurfaceView = (MySurfaceView) findViewById(R.id.my_surfaceView);
    }

}

結果演示(必須在真機上才能運行):
這裡寫圖片描述
看到沒,我的指北針多麼簡約大氣。

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved