方向传感器
Android 平台中,传感器框架通常是使用一个标准的三维坐标系来表示一个值的
要确定一个方向也需要一个三维坐标,毕竟我们的设备不可能永远都是水平端着的
Android 传感器返回的方向值就是一个长度为 3 的 flaot 数组,包含三个方向的值
方向 | 说明 |
---|---|
X 轴的方向 | 沿着屏幕水平方向从左到右,如果手机如果不是是正方形的话 较短的边需要水平放置,较长的边需要垂直放置 |
Y 轴的方向 | 从屏幕的左下角开始沿着屏幕的的垂直方向指向屏幕的顶端 |
Z 轴的方向 | 当水平放置时,指向天空的方向 |
方向传感器的三个值
从上一章节中我们知道,传感器的回调方法:onSensorChanged()
中的参数 SensorEvent event
,event 的值类型是 Float[]
的,而且最多只有三个元素,而方向传感器则刚好有三个元素,都表示度数
-
values[0]
方位角,手机绕着 Z 轴旋转的角度。0 表示正北 (North),90 表示正东 (East),180 表示正南(South),270 表示正西(West)
假如 values[0] 的值刚好是这四个值的话,并且手机沿水平放置的话,那么当前手机的正前方就是这四个方向,可以利用这一点来写一个指南针!
-
values[1]
倾斜角,手机翘起来的程度,当手机绕着x轴倾斜时该值会发生变化,取值范围是 [-180,180] 之间
-
假如把手机放在桌面上,而桌面是完全水平的话,values[1] 的则应该是 0,当然很少桌子是绝对水平的
-
从手机 顶部开始抬起 ,直到手机沿着 x 轴旋转180(此时屏幕向下水平放在桌面上),在这个旋转过程中,values[1] 的值会从 0 到 -180 之间变化,即手机抬起时,values[1] 的值会逐渐变小,知道等于 -180;
-
从手机 底部开始抬起 ,直到手机沿着 x 轴旋转 180 度,此时 values[1] 的值会 从 0 到 180 之间变化
我们可以利用 value[1] 的这个特性结合 value[2] 来实现一个平地尺!
-
-
value[2]
滚动角,沿着Y轴的滚动角度,取值范围为:[-90,90]
- 将手机屏幕朝上水平放在桌面上,这时如果桌面是平的,values[2] 的值应为 0
- 将手机从左侧逐渐抬起,values[2] 的值将逐渐减小,直到垂直于手机放置,此时 values[2]的值为 -90
- 从右侧则是 90
- 加入在垂直位置时继续向右或者向左滚动,values[2] 的值将会继续在 -90 到 90 之间变化
简单的 Demo
你可以拿着手机各种玩啥,看看三个值如何变换
-
创建一个 空的 Android 项目
cn.twle.android.OrientationSim
-
修改
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="16dp"> <TextView android:id="@+id/ms_x" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="方位角"/> <TextView android:id="@+id/ms_y" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="倾斜角" /> <TextView android:id="@+id/ms_z" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="滚动角" /> </LinearLayout>
-
修改 MainActivity.java 文件
package cn.twle.android.orientationsim; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.TextView; public class MainActivity extends AppCompatActivity implements SensorEventListener { private TextView ms_x,ms_y,ms_z; private SensorManager sm; private Sensor mSensorOrientation; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //获取传感器管理器 sm = (SensorManager) getSystemService(SENSOR_SERVICE); // 获取方向传感器 mSensorOrientation = sm.getDefaultSensor(Sensor.TYPE_ORIENTATION); //注册数值变化监听器 sm.registerListener(this, mSensorOrientation,SensorManager.SENSOR_DELAY_UI); ms_x = (TextView) findViewById(R.id.ms_x); ms_y = (TextView) findViewById(R.id.ms_y); ms_z = (TextView) findViewById(R.id.ms_z); } // 传感器数值变化会调用此方法 @Override public void onSensorChanged(SensorEvent event) { ms_x.setText("方位角:" + (float) (Math.round(event.values[0] * 100)) / 100); ms_y.setText("倾斜角:" + (float) (Math.round(event.values[1] * 100)) / 100); ms_z.setText("滚动角:" + (float) (Math.round(event.values[2] * 100)) / 100); } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } }
文字指南针
接下来我们写一个简单的文字版的指南针来体验方向传感器,当文字显示正南的时候,表示手机的正前方就是南方
文字版的超级没方向感,我都怀疑自己的认知了
-
创建一个 空的 Android 项目
cn.twle.android.Compass
-
这次我们尝试抛弃
activity_main.xml
,使用纯代码布局自定义一个
View
文件名MsCompassView.java
应该知道文件放哪里吧,作为教学,我一向怎么简单怎么来
package cn.twle.android.compass; import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.View; public class MsCompassView extends View implements Runnable{ private Paint mTextPaint; private int msWidth,msHeight; private float dec = 0.0f; private String msg = "正北 0°"; public MsCompassView(Context context) { this(context, null); } public MsCompassView(Context context, AttributeSet attrs) { super(context, attrs); // 获取屏幕高宽 Resources resources = this.getResources(); DisplayMetrics dm = resources.getDisplayMetrics(); msWidth = dm.widthPixels; msHeight = dm.heightPixels; // 初始化画笔 mTextPaint = new Paint(); mTextPaint.setColor(Color.GRAY); mTextPaint.setTextSize(64); mTextPaint.setStyle(Paint.Style.FILL); new Thread(this).start(); } public MsCompassView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawText(msg, msWidth / 4 , msWidth / 2, mTextPaint); } // 更新指南针角度 public void setDegree(float degree) { // 设置灵敏度 if(Math.abs(dec - degree) >= 2 ) { dec = degree; int range = 22; String degreeStr = String.valueOf(dec); // 指向正北 if(dec > 360 - range && dec < 360 + range) { msg = "正北 " + degreeStr + "°"; } // 指向正东 if(dec > 90 - range && dec < 90 + range) { msg = "正东 " + degreeStr + "°"; } // 指向正南 if(dec > 180 - range && dec < 180 + range) { msg = "正南 " + degreeStr + "°"; } // 指向正西 if(dec > 270 - range && dec < 270 + range) { msg = "正西 " + degreeStr + "°"; } // 指向东北 if(dec > 45 - range && dec < 45 + range) { msg = "东北 " + degreeStr + "°"; } // 指向东南 if(dec > 135 - range && dec < 135 + range) { msg = "东南 " + degreeStr + "°"; } // 指向西南 if(dec > 225 - range && dec < 225 + range) { msg = "西南 " + degreeStr + "°"; } // 指向西北 if(dec > 315 - range && dec < 315 + range) { msg = "西北 " + degreeStr + "°"; } } } @Override public void run() { while(!Thread.currentThread().isInterrupted()) { try { Thread.sleep(100); } catch(InterruptedException e) { Thread.currentThread().interrupt(); } postInvalidate(); } } }
-
修改文件
MainActivity.java
加载我们自定义的MsCompassView
package cn.twle.android.compass; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; public class MainActivity extends AppCompatActivity implements SensorEventListener { private MsCompassView ms_cview; private SensorManager sm; private Sensor mSensorOrientation; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ms_cview = new MsCompassView(MainActivity.this); sm = (SensorManager) getSystemService(SENSOR_SERVICE); mSensorOrientation = sm.getDefaultSensor(Sensor.TYPE_ORIENTATION); sm.registerListener(this, mSensorOrientation, SensorManager.SENSOR_DELAY_UI); setContentView(ms_cview); } @Override public void onSensorChanged(SensorEvent event) { ms_cview.setDegree(event.values[0]); } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } @Override protected void onDestroy() { super.onDestroy(); sm.unregisterListener(this); } }