在上一篇博客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:由于模拟器上面没有传感器,所以本项目必须要用真机才能看到效果。