android 水准仪的实现(方向传感器的使用)

好久之前就已经研究了方向传感器Sensor.TYPE_ORIENTATION。根据自已实践,改写了网上的两个水准仪的例子,又重新封装使用了一下,最后也用在了项目中。

1、前言介绍

下面这段话是出自Android 传感器之方向传感器

一般情况下,在android系统中获取手机的方位信息在api中有TYPE_ORIENTATION常量,可以像得到加速度传感器那样得到方向传感器sm.getDefaultSensor(Sensor.TYPE_ORIENTATION);然而我们这样做的话在最新版的SDK中就会看到这么一句话:“TYPE_ORIENTATION   This constant is deprecated. use SensorManager.getOrientation() instead. ”即这种方式也过期,不建议使用!Google建议我们在应用程序中使用SensorManager.getOrientation()来获得原始数据。

public static float[] getOrientation (float[] R, float[] values)

第一个参数是R[] 是一个旋转矩阵,用来保存磁场和加速度的数据,可以理解为这个函数的传入值,通过它这个函数给你求出方位角。

第二个参数就是这个函数的输出了,他有函数自动为我们填充,这就是我们想要的。

values[0]  :方向角,但用(磁场+加速度)得到的数据范围是(-180~180),也就是说,0表示正北,90表示正东,180/-180表示正南,-90表示正西。而直接通过方向感应器数据范围是(0~359)360/0表示正北,90表示正东,180表示正南,270表示正西。

values[1]  pitch 倾斜角  即由静止状态开始,前后翻转,手机顶部往上抬起(0~-90),手机尾部往上抬起(0~90)

values[2]  roll 旋转角 即由静止状态开始,左右翻转,手机左侧抬起(0~90),手机右侧抬起(0~-90)

2、示例代码一

package com.level.level1;import com.level.level1.R;import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;public class LevelView extends View
{// 定义水平仪大圆盘图片Bitmap compass;// 定义水平仪中的气泡图标Bitmap ball;// 定义水平仪中气泡 的X、Y座标int ballX, ballY;// 定义气泡位于中间时(水平仪完全水平),气泡的X、Y座标int cx, cy;// 定义水平仪大圆盘中心座标X、Yint backCx;int backCy;// 定义灵敏度,即水平仪能处理的最大倾斜角,超过该角度,气泡将直接在位于边界。int SENSITIVITY = 30;public LevelView(Context context, AttributeSet attrs){super(context, attrs);// 加载水平仪大圆盘图片和气泡图片compass = BitmapFactory.decodeResource(getResources(), R.drawable.back);ball = BitmapFactory.decodeResource(getResources(), R.drawable.small);// 计算出 水平仪完全水平时 气泡位置  左上角为原点cx = (compass.getWidth() - ball.getWidth()) / 2;cy = (compass.getHeight() - ball.getHeight()) / 2;// 计算出水平仪大圆盘中心座标X、Y  左上角为原点backCx = compass.getWidth() / 2;backCy = compass.getWidth() / 2;}@Overrideprotected void onDraw(Canvas canvas){super.onDraw(canvas);// 绘制水平仪大圆盘图片canvas.drawBitmap(compass, 0, 0, null);// 根据气泡坐标绘制气泡canvas.drawBitmap(ball, ballX, ballY, null);}public void onChangeXY(int zAngle,int yAngle,int xAngle){// 定义气泡当前位置X Y坐标值int x, y;x = cx;y = cy;// 如果沿x轴的倾斜角  在最大角度之内则计算出其相应坐标值if (Math.abs(xAngle) <= SENSITIVITY) {// 根据与x轴的倾斜角度计算X座标的变化值(倾斜角度越大,X座标变化越大)int deltaX = (int) (cx * xAngle / SENSITIVITY);x += deltaX;}// 如果沿x轴的倾斜角已经大于MAX_ANGLE,气泡应到最左边else if (xAngle > SENSITIVITY) {x = 0;}// 如果与x轴的倾斜角已经小于负的MAX_ANGLE,气泡应到最右边else {x = cx * 2;}// 如果沿Y轴的倾斜角还在最大角度之内if (Math.abs(yAngle) <= SENSITIVITY) {// 根据沿Y轴的倾斜角度计算Y座标的变化值(倾斜角度越大,Y座标变化越大)int deltaY = (int) (cy * yAngle / SENSITIVITY);y += deltaY;}// 如果沿Y轴的倾斜角已经大于MAX_ANGLE,气泡应到最下边else if (yAngle > SENSITIVITY) {y = cy * 2;}// 如果沿Y轴的倾斜角已经小于负的MAX_ANGLE,气泡应到最右边else {y = 0;}// 如果计算出来的X、Y座标还位于水平仪的仪表盘内,更新水平仪的气泡座标if (isContain(x, y)) {ballX = x;ballY = y;} else {// 有待后续继续完成}//重绘界面invalidate();}// 计算x、y点的气泡是否处于水平仪的大圆盘内private boolean isContain(int x, int y) {// 计算气泡的圆心座标X、Yint ballCx = x + ball.getWidth() / 2;int ballCy = y + ball.getWidth() / 2;// 计算气泡的圆心与水平仪大圆盘中中心之间的距离。double distance = Math.sqrt((ballCx - backCx) * (ballCx - backCx)+ (ballCy - backCy) * (ballCy - backCy));// 若两个圆心的距离小于它们的半径差,即可认为处于该点的气泡依然位于仪表盘内if (distance < cx) {return true;} else {return false;}}
}

package com.level.level1;import com.level.level1.R;import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.app.Activity;
import android.widget.TextView;public class MyGradienter extends Activity implements SensorEventListener {// 定义水平仪的仪大圆盘private LevelView myview;// 定义Sensor管理器SensorManager mySM;// 定义显示栏 显示X Y Z轴方向转过角度与当前方位private TextView tx, ty, tz, td;private Sensor acc_sensor;private Sensor mag_sensor;// 加速度传感器数据float accValues[] = new float[3];// 地磁传感器数据float magValues[] = new float[3];// 旋转矩阵,用来保存磁场和加速度的数据float r[] = new float[9];// 模拟方向传感器的数据(原始数据为弧度)float values[] = new float[3];@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_my_gradienter);myview = (LevelView) findViewById(R.id.myview);tx = (TextView) findViewById(R.id.testviewx);ty = (TextView) findViewById(R.id.testviewy);tz = (TextView) findViewById(R.id.testviewz);td = (TextView) findViewById(R.id.testviewd);// 获取手机传感器管理服务mySM = (SensorManager) getSystemService(SENSOR_SERVICE);}@Overridepublic void onResume() {super.onResume();acc_sensor = mySM.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);mag_sensor = mySM.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);// 给传感器注册监听:mySM.registerListener(this, acc_sensor, SensorManager.SENSOR_DELAY_GAME);mySM.registerListener(this, mag_sensor, SensorManager.SENSOR_DELAY_GAME);}@Overrideprotected void onPause() {// 取消方向传感器的监听mySM.unregisterListener(this);super.onPause();}@Overrideprotected void onStop() {// 取消方向传感器的监听mySM.unregisterListener(this);super.onStop();}@Overridepublic void onAccuracyChanged(Sensor arg0, int arg1) {// TODO Auto-generated method stub}@Overridepublic void onSensorChanged(SensorEvent event) {// 获取手机触发event的传感器的类型int sensorType = event.sensor.getType();switch (sensorType) {case Sensor.TYPE_ACCELEROMETER:accValues = event.values.clone();break;case Sensor.TYPE_MAGNETIC_FIELD:magValues = event.values.clone();break;}SensorManager.getRotationMatrix(r, null, accValues, magValues);SensorManager.getOrientation(r, values);// 获取 沿着Z轴转过的角度int zAngle = (int) Math.toDegrees(values[0]);tz.setText("Z轴方向转过的角度:" + zAngle);// 显示当前的方位displayCompass(zAngle);// 获取 沿着X轴倾斜时 与Y轴的夹角int yAngle = (int) Math.toDegrees(values[1]);ty.setText("Y轴方向翘起的角度:" + yAngle);// 获取 沿着Y轴的滚动时 与X轴的角度int xAngle = (int) Math.toDegrees(values[2]);tx.setText("x轴方向翘起的角度:" + xAngle);myview.onChangeXY(zAngle,yAngle,xAngle);}private void displayCompass(int angle) {if ((angle < 22.5) || (angle > 337.5))td.setText("手机顶部当前方位: 北");if ((angle > 22.5) && (angle < 67.5))td.setText("手机顶部当前方位: 西北");if ((angle > 67.5) && (angle < 112.5))td.setText("手机顶部当前方位: 西");if ((angle > 112.5) && (angle < 157.5))td.setText("手机顶部当前方位: 西北");if ((angle > 157.5) && (angle < 202.5))td.setText("手机顶部当前方位: 南");if ((angle > 202.5) && (angle < 247.5))td.setText("手机顶部当前方位: 东南");if ((angle > 247.5) && (angle < 292.5))td.setText("手机顶部当前方位: 东");if ((angle > 292.5) && (angle < 337.5))td.setText("手机顶部当前方位: 东北");}
}

3、示例代码二

package com.tcjt.level2;import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;public class MainView extends View {Paint paint = new Paint(); //画笔Bitmap shangBitmap1; //上面的大矩形图Bitmap shangBitmap2; //上面的气泡Bitmap zuoBitmap1; //左面的大矩形图Bitmap zuoBitmap2; //左面图的气泡Bitmap zhongBitmap1; //中间的大圆图Bitmap zhongBitmap2; //中间的小气泡Bitmap xiaBitmap1; //右下的矩形图Bitmap xiaBitmap2; //右下的气泡//背景矩形的位置声明int shang1_X = 60; //上面的大矩形图int shang1_Y = 12;int zuo1_X = 12; //左面的大矩形图int zuo1_Y = 60;int zhong1_X = 65; //中间的大圆图int zhong1_Y = 65;int xia1_X = 145; //右下的矩形图int xia1_Y = 145;//水泡的位置声明int shang2_X; //上面的气泡XY 坐标int shang2_Y;int zuo2_X; //左面图的气泡XY 坐标int zuo2_Y;int zhong2_X; //中间的小气泡XY 坐标int zhong2_Y;int xia2_X; //右下的气泡XY 坐标int xia2_Y;public MainView(Context context, AttributeSet attrs){super(context, attrs);initBitmap(); //初始化图片资源initLocation(); //初始化气泡的位置}private void initBitmap(){ //初始化图片的方法//该处省略了部分代码,将在后面进行介绍shangBitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.level_shang1);shangBitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.level_shang2);zuoBitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.level_zuo1);zuoBitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.level_zuo2);zhongBitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.level_zhong1);zhongBitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.level_zhong2);xiaBitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.level_xia1);xiaBitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.level_xia2);}private void initLocation(){ //初始化气泡位置的方法//该处省略了部分代码,将在后面进行介绍shang2_X = shang1_X + shangBitmap1.getWidth()/2- shangBitmap2.getWidth()/2;shang2_Y = shang1_Y + shangBitmap1.getHeight()/2- shangBitmap2.getHeight()/2;zuo2_X = zuo1_X + zuoBitmap1.getWidth()/2- zuoBitmap2.getWidth()/2;zuo2_Y = zuo1_Y + zuoBitmap1.getHeight()/2- zuoBitmap2.getHeight()/2;zhong2_X = zhong1_X + zhongBitmap1.getWidth()/2- zhongBitmap2.getWidth()/2;zhong2_Y = zhong1_Y + zhongBitmap1.getHeight()/2- zhongBitmap2.getHeight()/2;xia2_X = xia1_X + xiaBitmap1.getWidth()/2- xiaBitmap2.getWidth()/2;xia2_Y = xia1_Y + xiaBitmap1.getHeight()/2- xiaBitmap2.getHeight()/2;}@Overrideprotected void onDraw(Canvas canvas){//重写的绘制方法super.onDraw(canvas);//该处省略了部分代码,将在后面进行介绍canvas.drawColor(Color.WHITE); //设置背景色为白色paint.setColor(Color.BLUE); //设置画笔颜色paint.setStyle(Style.STROKE); //设置画笔为不填充canvas.drawRect(5, 5, 315, 315, paint);//绘制外边框矩形//画背景矩形canvas.drawBitmap(shangBitmap1, shang1_X,shang1_Y, paint); //上canvas.drawBitmap(zuoBitmap1, zuo1_X,zuo1_Y, paint); //左canvas.drawBitmap(zhongBitmap1, zhong1_X,zhong1_Y, paint); //中canvas.drawBitmap(xiaBitmap1, xia1_X,xia1_Y, paint); //下//开始绘制气泡canvas.drawBitmap(shangBitmap2, shang2_X,shang2_Y, paint); //上canvas.drawBitmap(zuoBitmap2, zuo2_X,zuo2_Y, paint); //左canvas.drawBitmap(zhongBitmap2, zhong2_X,zhong2_Y, paint); //中canvas.drawBitmap(xiaBitmap2, xia2_X, xia2_Y, paint);//下paint.setColor(Color.GRAY);//设置画笔颜色用来绘制刻度//绘制上面方框中的刻度canvas.drawLine (shang1_X+shangBitmap1.getWidth()/2-7,shang1_Y, shang1_X+shangBitmap1.getWidth()/2-7,shang1_Y+shangBitmap1.getHeight()-2, paint);canvas.drawLine (shang1_X+shangBitmap1.getWidth()/2+7,shang1_Y, shang1_X+shangBitmap1.getWidth()/2+7,shang1_Y+shangBitmap1.getHeight()-2, paint);//绘制左面方框中的刻度canvas.drawLine(zuo1_X,zuo1_Y+zuoBitmap1.getHeight()/2-7,zuo1_X+zuoBitmap1.getWidth()-2,zuo1_Y+zuoBitmap1.getHeight()/2-7, paint);canvas.drawLine(zuo1_X,zuo1_Y+zuoBitmap1.getHeight()/2+7,zuo1_X+zuoBitmap1.getWidth()-2,zuo1_Y+zuoBitmap1.getHeight()/2+7, paint);//绘制下面方框中的刻度canvas.drawLine(xia1_X+xiaBitmap1.getWidth()/2-10,xia1_Y+xiaBitmap1.getHeight()/2-20,xia1_X+xiaBitmap1.getWidth()/2+20,xia1_Y+xiaBitmap1.getHeight()/2+10, paint);canvas.drawLine(xia1_X+xiaBitmap1.getWidth()/2-20,xia1_Y+xiaBitmap1.getHeight()/2-10,xia1_X+xiaBitmap1.getWidth()/2+10,xia1_Y+xiaBitmap1.getHeight()/2+20, paint);//中间圆圈中的刻度(小圆)RectF oval = new RectF(zhong1_X+zhongBitmap1.getWidth()/2-10,zhong1_Y+zhongBitmap1.getHeight()/2-10,zhong1_X+zhongBitmap1.getWidth()/2+10,zhong1_Y+zhongBitmap1.getHeight()/2+10);canvas.drawOval(oval, paint);//绘制基准线(圆)}
}

package com.tcjt.level2;import android.hardware.SensorListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;@SuppressWarnings("deprecation")
public class LevelActivity extends Activity {int k = 45; //灵敏度MainView mv;//真机SensorManager mySensorManager;//测试时
//	SensorManagerSimulator mySensorManager; @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_level);mv = (MainView) findViewById(R.id.mainView);mySensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);//真机}private final SensorListener mSensorLisener =new SensorListener(){@Overridepublic void onAccuracyChanged(int sensor, int accuracy) { }public boolean isContain(int x, int y){//判断点是否在圆内int tempx =(int) (x + mv.zhongBitmap2.getWidth()/2.0);int tempy =(int) (y + mv.zhongBitmap2.getWidth()/2.0);int ox = (int) (mv.zhong1_X+ mv.zhongBitmap1.getWidth()/2.0);int oy = (int) (mv.zhong1_X+ mv.zhongBitmap1.getWidth()/2.0);if(Math.sqrt((tempx-ox)*(tempx-ox)+(tempy-oy)*(tempy-oy))>(mv.zhongBitmap1.getWidth()/2.0-mv.zhongBitmap2.getWidth()/2.0)){//不在圆内return false;}else{//在圆内时return true;}}@Overridepublic void onSensorChanged(int sensor, float[] values) {if(sensor == SensorManager.SENSOR_ORIENTATION){double pitch = values[SensorManager.DATA_Y];double roll = values[SensorManager.DATA_Z];int x=0; int y=0;//临时变量,算中间水泡坐标时用int tempX=0; int tempY=0;//下面气泡的临时变量//开始调整x 的值if(Math.abs(roll)<=k){mv.shang2_X = mv.shang1_X //上面的+ (int)(((mv.shangBitmap1.getWidth()-mv.shangBitmap2.getWidth())/2.0)-(((mv.shangBitmap1.getWidth()-mv.shangBitmap2.getWidth())/2.0)*roll)/k);x = mv.zhong1_X //中间的+ (int)(((mv.zhongBitmap1.getWidth()-mv.zhongBitmap2.getWidth())/2.0)-(((mv.zhongBitmap1.getWidth()-mv.zhongBitmap2.getWidth())/2.0)*roll)/k);}else if(roll>k){mv.shang2_X=mv.shang1_X; x = mv.zhong1_X;}else{mv.shang2_X=mv.shang1_X+mv.shangBitmap1.getWidth()- mv.shangBitmap2.getWidth();x = mv.zhong1_X+ mv.zhongBitmap1.getWidth()- mv.zhongBitmap2.getWidth();}//开始调整y 的值if(Math.abs(pitch)<=k){mv.zuo2_Y=mv.zuo1_Y //左面的+ (int)(((mv.zuoBitmap1.getHeight()-mv.zuoBitmap2.getHeight())/2.0)+(((mv.zuoBitmap1.getHeight()-mv.zuoBitmap2.getHeight())/2.0)*pitch)/k);y =mv.zhong1_Y+ //中间的(int)(((mv.zhongBitmap1.getHeight()-mv.zhongBitmap2.getHeight())/2.0)+(((mv.zhongBitmap1.getHeight()-mv.zhongBitmap2.getHeight())/2.0)*pitch)/k);}else if(pitch>k){mv.zuo2_Y=mv.zuo1_Y+mv.zuoBitmap1.getHeight()-mv.zuoBitmap2.getHeight();y=mv.zhong1_Y+mv.zhongBitmap1.getHeight()-mv.zhongBitmap2.getHeight();}else{mv.zuo2_Y = mv.zuo1_Y; y = mv.zhong1_Y;}//下面的tempX = -(int) (((mv.xiaBitmap1.getWidth()/2-28)*roll+(mv.xiaBitmap1.getWidth()/2-28)*pitch)/k);tempY = -(int) ((-(mv.xiaBitmap1.getWidth()/2-28)*roll-(mv.xiaBitmap1.getWidth()/2-28)*pitch)/k);//限制下面的气泡范围if(tempY>mv.xiaBitmap1.getHeight()/2-28){tempY = mv.xiaBitmap1.getHeight()/2-28;}if(tempY < -mv.xiaBitmap1.getHeight()/2+28){tempY = -mv.xiaBitmap1.getHeight()/2+28;}if(tempX > mv.xiaBitmap1.getWidth()/2-28){tempX = mv.xiaBitmap1.getWidth()/2-28;}if(tempX < -mv.xiaBitmap1.getWidth()/2+28){tempX = -mv.xiaBitmap1.getWidth()/2+28;}mv.xia2_X = tempX + mv.xia1_X + mv.xiaBitmap1.getWidth()/2 -mv.xiaBitmap2.getWidth()/2;mv.xia2_Y = tempY + mv.xia1_Y + mv.xiaBitmap1.getHeight()/2 - mv.xiaBitmap2.getWidth()/2;if(isContain(x, y)){//中间的水泡在圆内才改变坐标mv.zhong2_X = x; mv.zhong2_Y = y;}mv.postInvalidate();//重绘MainView}} //传感器监听器类//该处省略了部分代码,将在后面进行介绍};@Overrideprotected void onResume(){ //添加监听mySensorManager.registerListener(mSensorLisener,SensorManager.SENSOR_ORIENTATION);super.onResume();}@Overrideprotected void onPause() { //取消监听mySensorManager.unregisterListener (mSensorLisener);super.onPause();}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.activity_level, menu);return true;}}

两个例子都能实现水准仪的效果。图片我就不贴,可以参考里面的代码。

博客代码下载:http://download.csdn.net/detail/qq_16064871/9743465。有所封装代码。

参考文章:
 

Android 传感器之方向传感器


关于Android 传感器坐标与读数的进一步讨论







本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部