在上一篇博客Android Draw 实现时钟的基础上,实现了Android版的指南针,当然,这是比较简陋的,只是纯属当做练习Android的Canvas罢了。首先,要做指南针,必须用到我们Android上内置的方向传感器,当然要保持水平的状态的话也可以用到重力传感器。用传感器的格式非常固定,都是一样的套路,就和广播接收器一样,得先注册,然后写一个监听器,最后要在程序退出销毁前注销监听器。步骤如下:
1. 在onCreate函数注册传感器。其中的listener是我们设置的

//获取传感器管理器
sensorManager =(SensorManager)getSystemService(SENSOR_SERVICE);
 //获取重力传感器
Sensor acceler =sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sensorManager.registerListener(listener, acceler, SensorManager.SENSOR_DELAY_GAME);
//获取方向传感器
Sensor orient =sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
sensorManager.registerListener(listener, orient, SensorManager.SENSOR_DELAY_GAME);

2.为传感器设置一个监听器,监听其内容发生的变化。里面要重写两个函数

private SensorEventListener listener =new SensorEventListener() {
        boolean Switch = true;
        @SuppressWarnings("deprecation")
        @Override
        public void onSensorChanged(SensorEvent event) {

        }
        @Override
        public void onAccuracyChanged(Sensor sensor, int i) {
            Log.d(TAG, "onAccuracyChanged: "+i);
        }
    };

3.最后在destroy函数里面取消注册传感器

@Override
    protected void onDestroy() {
        super.onDestroy();
        //注销所有传感器
        sensorManager.unregisterListener(listener);
    }

有了上面的方向传感器和重力传感器之后,我们就可以获取到其想关的参数,在传感器onSensorChanged函数里面我们可以用下面三句话获取传感器在三维空间的值。

float x = event.values[SensorManager.DATA_X];
   float y = event.values[SensorManager.DATA_Y];
   float z = event.values[SensorManager.DATA_Z];

在重力传感器里面上面这三个X,Y,Z的值再通过下面这个固定写法求出三个方向上的重力加速度

float alpha = 0.8f;
   gravity[0] = alpha * gravity[0] + (1 - alpha) * x;
   gravity[1] = alpha * gravity[1] + (1 - alpha) * y;
   gravity[2] = alpha * gravity[2] + (1 - alpha) * z;

大概通过下面的对x,y,z的控制,控制达到水平状态,参数都是通过微调出来的,不够精确,大家也可以做出相应调整

//重力加速度
    x = gravity[0];
    y = gravity[1];
    z = gravity[2];

    double xs =-0.50;
    double xa =0.50;
    double ys =-0.50;
    double ya =0.50;
    double zs =9.8;
    double za =10.4;
    if((x>=xs&&x<=xa)&&(y>=ys&&y<=ya)&&(z>=zs&&z<=za)){
         //"水平状态"
     } else {
        //"倾斜状态,请先置水平"
     }

然后通过方向传感器的话,主要是获取到X方向的值就可以确定指南针的方向了

//这个是指南针的方向
double X = Math.floor(x);

下面就是通过Canvas画出来的问题了,下面的源码,里面有注释

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.Window;

/**
 *  Compass 指南针
 */
public class MainActivity extends Activity {
    private static final String TAG="MainActivity";
    private Paint paint;
    private int begin=0;
    private SensorManager sensorManager;
    private float gravity[]=new float[3];
    private float linear_accelerometer[] =new float[3];
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        CustomView customView=new CustomView(this);
        setContentView(customView);
        //获取传感器管理器
        sensorManager =(SensorManager)getSystemService(SENSOR_SERVICE);
    }
    @Override
    protected void onResume() {
        super.onResume();
        //获取重力传感器
        Sensor acceler =sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        sensorManager.registerListener(listener, acceler, SensorManager.SENSOR_DELAY_GAME);
        //获取方向传感器
        @SuppressWarnings("deprecation")
        Sensor orient =sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
        sensorManager.registerListener(listener, orient, SensorManager.SENSOR_DELAY_GAME);
    }
    @Override
    protected void onPause() {
        super.onPause();
        //注销所有传感器
        sensorManager.unregisterListener(listener);
    }
    private SensorEventListener listener =new SensorEventListener() {
        boolean Switch = true;
        @SuppressWarnings("deprecation")
        @Override
        public void onSensorChanged(SensorEvent event) {
            float x = event.values[SensorManager.DATA_X];
            float y = event.values[SensorManager.DATA_Y];
            float z = event.values[SensorManager.DATA_Z];
            switch (event.sensor.getType()) {
                case Sensor.TYPE_ACCELEROMETER:
                    float alpha = 0.8f;
                    gravity[0] = alpha * gravity[0] + (1 - alpha) * x;
                    gravity[1] = alpha * gravity[1] + (1 - alpha) * y;
                    gravity[2] = alpha * gravity[2] + (1 - alpha) * z;

                    //重力加速度
                    x = gravity[0];
                    y = gravity[1];
                    z = gravity[2];

                    double xs =-0.50;
                    double xa =0.50;
                    double ys =-0.50;
                    double ya =0.50;
                    double zs =9.8;
                    double za =10.4;
                    if((x>=xs&&x<=xa)&&(y>=ys&&y<=ya)&&(z>=zs&&z<=za)){
//                        accelerometer.setText("水平状态");
                        Switch = true;
                    } else {
//                        accelerometer.setText("倾斜状态,请先置水平");
                        Switch = false;
                    }


                    break;
                case Sensor.TYPE_ORIENTATION:
                //这是是水平状态才去看方向的变化,因为水平状态才保证方向是正确的
                    if (Switch) {
                        double X = Math.floor(x);
                        int range = 22;
                        int deg=180;
                        // 指向正北
                        if(X > 360 - range && X < 360 + range)
                        {
                            begin= (int) (X-170);
                        }
                        if(X > 330 - range && X < 350)
                        {
                            begin= (int) (X-150);
                        }
                        // 指向正东
                        if(X > 90 - range && X < 90 + range)
                        {
                            begin= (int) (deg-X);
                        }
                        // 指向正南
                        if(X > 180 - range && X < 180 + range)
                        {
                            begin= (int) (X-deg);
                        }
                        if(X > 190 && X < 207 )
                        {
                            begin= (int) (X-deg-20);
                        }
                        // 指向正西
                        if(X > 270 - range && X < 270 + range)
                        {
                            begin= (int) (deg-X);
                        }
                        // 东偏北
                        if(X > 0 && X < 45)
                        {
                            begin= (int) (deg-X);
                        }
                        // 指向东北
                        if(X > 45 - range && X < 45 + range)
                        {
                            begin= (int) (deg-X);
                        }
                        // 指向东南
                        if(X > 135 - range && X < 135 + range)
                        {
                            begin= (int) (deg-X);
                        }
                        //东南
                        if(X > 150  && X < 180)
                        {
                            begin= (int) (X-140);
                        }
                        // 指向西南
                        if(X > 225 - range && X < 225 + range)
                        {
                            begin= (int) (deg-X);
                        }
                        // 指向西北
                        if(X > 315 - range && X < 315 + range)
                        {
                            begin= (int) (deg-X);
                        }
                    }
                    break;
            }
        }
        @Override
        public void onAccuracyChanged(Sensor sensor, int i) {
            Log.d(TAG, "onAccuracyChanged: "+i);
        }
    };
    class CustomView extends View {
        public CustomView(Context context) {
            super(context);
            //new 一个画笔
            paint=new Paint();
            //设置画笔颜色
            paint.setColor(Color.YELLOW);
            //设置结合处的样子,Miter:结合处为锐角, Round:结合处为圆弧:BEVEL:结合处为直线。
            paint.setStrokeJoin(Paint.Join.ROUND);
            //设置画笔笔刷类型 如影响画笔但始末端
            paint.setStrokeCap(Paint.Cap.ROUND);
            //设置画笔宽度
            paint.setStrokeWidth(3);

        }
        @Override
        public void draw(Canvas canvas) {
            double startTime=System.currentTimeMillis();   //获取开始时间
            super.draw(canvas);
            //设置屏幕颜色,也可以利用来清屏。
            canvas.drawColor(Color.rgb(122,65,255));
            paint.setAntiAlias(true);
            paint.setStyle(Paint.Style.STROKE);
            canvas.translate(canvas.getWidth()/2, canvas.getHeight()/2); //将画布移动到屏幕中心
            canvas.drawCircle(0, 0, 300, paint); //画圆圈
            Paint tmpPaint = new Paint(paint); //小刻度画笔对象
            tmpPaint.setStrokeWidth(1);//设置画笔笔尖的粗细
            float  y=300;   //向Y方向移动画笔的位置
            float  x=300;   //向X方向移动画笔的位置
            int count = 360; //总刻度数
            canvas.save();//各个状态最初,是下次第一个canvas.restore()返回点
            canvas.rotate(begin,0f,0f); //旋转画纸
            for(int i=0 ; i <count ; i++){
                if(i%5 == 0){
                    //画刻度
                    canvas.drawLine(0f, y, 0, y+12f, paint);
                    //画刻度的数字
                    canvas.drawText(String.valueOf(i), -4f, y+25f, tmpPaint);
                }else{
                    canvas.drawLine(0f, y, 0f, y +5f, tmpPaint);
                }
                //每一个循环就旋转一个刻度,可以想象一下就是笔不动,下面的纸旋转,那么下一次画的位置就发生改变了
                canvas.rotate(360/count,0f,0f); //旋转画纸
            }
            canvas.restore();
            tmpPaint.setColor(Color.GRAY);
            //设置画笔宽度
            tmpPaint.setStrokeWidth(4);
            canvas.drawCircle(0, 0, 7, tmpPaint);
            tmpPaint.setStyle(Paint.Style.FILL);
            tmpPaint.setColor(Color.YELLOW);
            canvas.drawCircle(0, 0, 5, tmpPaint);
            canvas.rotate(-30,0f,0f); //调整画布的位置
            tmpPaint.setColor(Color.RED);
            //设置画笔宽度
            tmpPaint.setStrokeWidth(6);
            canvas.rotate(begin+210,0f,0f); //旋转画纸
            canvas.drawLine(0, -10, 0, 250, tmpPaint);
            canvas.rotate(360/12/5);
            //设置这个是为了避免上面一系列的计算的时间影响
            double endTime=System.currentTimeMillis(); //获取结束时间
            //刷新页面
            postInvalidateDelayed((long) (1-(endTime-startTime)));
            tmpPaint.setTextSize(25);
            tmpPaint.setStrokeWidth(10);
            tmpPaint.setColor(Color.WHITE);
            //画东南西北的标记
            canvas.drawText("South", 0f, y+50f, tmpPaint);
            canvas.drawText("North", -50f, -y-50f, tmpPaint);
            canvas.drawText("East", x+50f, -40f, tmpPaint);
            canvas.drawText("West", -x-100f, 50f, tmpPaint);
        }
    }
}

整个工程项目地址:
https://github.com/huazhouwujinbiao/Demo_Canvs2.git

Tip:由于模拟器上面没有传感器,所以本项目必须要用真机才能看到效果。