android 水准仪的实现(方向传感器的使用)
好久之前就已经研究了方向传感器Sensor.TYPE_ORIENTATION。根据自已实践,改写了网上的两个水准仪的例子,又重新封装使用了一下,最后也用在了项目中。
1、前言介绍
下面这段话是出自Android 传感器之方向传感器
一般情况下,在android系统中获取手机的方位信息在api中有TYPE_ORIENTATION常量,可以像得到加速度传感器那样得到方向传感器sm.getDefaultSensor(Sensor.TYPE_ORIENTATION);然而我们这样做的话在最新版的SDK中就会看到这么一句话:“TYPE_ORIENTATIONThis 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、Y int 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; } @Override protected 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、Y int 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]; @Override protected 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); } @Override public 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); } @Override protected void onPause() { // 取消方向传感器的监听 mySM.unregisterListener(this); super.onPause(); } @Override protected void onStop() { // 取消方向传感器的监听 mySM.unregisterListener(this); super.onStop(); } @Override public void onAccuracyChanged(Sensor arg0, int arg1) { // TODO Auto-generated method stub } @Override public 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 ((a