Android系统提供了对传感器的支持,如果手机设备的硬件提供了这些传感器,Android应用可以通过传感器来获取设备的外界条件,包括手机设备的运行状态、当前摆放方向、外界的磁场、温度和压力等。Android系统提供了驱动程序去管理这些传感器硬件,当传感器硬件感知到外部环境发生改变时,Android系统负责管理这些传感器数据。
获得设备支持的所有传感器
基于Android平台开放性,尽管Google为Android提供了统一的API接口用于支持各式各样的传感器,但每家开发商生产的手机对传感器的种类支持不尽相同,有的手机厂商甚至开发出第三方的传感器以丰富用户体验,开发者可以通过代码查询手机上所支持的传感器数量及种类:
SensorManager sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
// 获取手机上所有的传感器
List<Sensor> sensorlist = sensorManager.getSensorList(Sensor.TYPE_ALL);
//获得设备所支持的传感器数量
Log.i(TAG, sensorlist.size() + "");
for (int i = 0; i < sensorlist.size(); i++) {
//获得传感器类别
Log.i(TAG, "传感器" + (i + 1) + ":" + sensorlist.get(i));
}
在型号为HTC M8st的手机设备上运行上段代码,打印的log如下所示:
可见,该型号的手机设备支持18种传感器,其中比较主要的是:
- 加速度传感器(Accelerometer Sensor),通过Sensor.TYPE_ACCLEROMETER获得;
- 磁力传感器(Magnetic field Sensor),通过Sensor.TYPE_MAGNETIC_FIELD获得;
- 陀螺仪传感器(Gyroscope Sensor),通过Sensor.TYPE_GYROSCOPE获得;
- 光传感器(Light Sensor),通过Sensor.TYPE_LIGHT获得;
- 方向传感器(Orientation Sensor),通过Sensor.TYPE_ORIENTATION获得;
- 线性加速器(Linear Acceleration);
- 重力传感器(Gravity);
注:其中Accelerometer Sensor = Gravity+ Linear Acceleration
光传感器的使用
传感器的用法基本类似,下面将介绍光传感器的用法。
private sensorListener listener;
private SensorManager sensorManager;
private Sensor sensor;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 获得传感器服务
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
// 得到光线传感器
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);// 得到光线传感器
listener = new sensorListener();
}
//在onResume回调方法中为SensorManager绑定监听器
@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
//为SensorManager绑定监听器,第三个参数是获取传感器数据的频度,频度越低,越省电,但越不精确;频度越高,越精确,但越费电,SensorManager.SENSOR_DELAY_UI表示频度最低的情形
sensorManager.registerListener(listener, sensor,
sensorManager.SENSOR_DELAY_UI);
}
//实现SensorEventListener接口
private class sensorListener implements SensorEventListener {
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
//获取对应类型传感器得到的数据
@Override
public void onSensorChanged(SensorEvent event) {
//光强度单位是勒克斯,数据存在回调方法的参数SensorEvent.values[0]中
float lux = event.values[0];
Log.i(TAG,"光线强度:" + lux);
}
}
//为节省资源和电量,应在onPause、onStop、OnDestroy方法中取消注册监听器
@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
sensorManager.unregisterListener(listener);
listener = null;
}
@Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
sensorManager.unregisterListener(listener);
listener = null;
}
@Override
protected void onDestroy() {
super.onDestroy();
sensorManager.unregisterListener(listener);
listener = null;
}
加速度传感器的使用
加速度a表示物体在单位时间内的速率变化,根据牛顿第二定律可知:∑F=ma。即物体的加速度=物体所受到力的矢量和/物体的质量。下段代码演示了利用加速度传感器获得手机设备在三维坐标系三个轴的正方向的加速度:
private final static String TAG = "MainActivity";
private SensorManager manager;
private sensorlistener listener;
private float[] gravity = new float[3];
private float[] linear_acceleration = new float[3];
private Sensor sensor;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
manager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = manager.getDefaultSensor(Sensor.TYPE_ORIENTATION);// 得到加速度传感器
listener = new sensorlistener();
}
@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
// 注册一个监听器
manager.registerListener(listener, sensor, manager.SENSOR_DELAY_UI);
}
// 监听器
private class sensorlistener implements SensorEventListener {
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
@Override
public void onSensorChanged(SensorEvent event) {
float x = event.values[0];
float y = event.values[1];
float z = event.values[2];
final float alpha = 0.8f;
// Isolate the force of gravity with the low-pass filter.
gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0];
gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1];
gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2];
// Remove the gravity contribution with the high-pass filter.
linear_acceleration[0] = event.values[0] - gravity[0];
linear_acceleration[1] = event.values[1] - gravity[1];
linear_acceleration[2] = event.values[2] - gravity[2];
Log.i(TAG, "x:" + linear_acceleration[0]);
Log.i(TAG, "y:" + linear_acceleration[1]);
Log.i(TAG, "z:" + linear_acceleration[2]);
}
}
@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
manager.unregisterListener(listener);
listener = null;
}
@Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
manager.unregisterListener(listener);
listener = null;
}
@Override
protected void onDestroy() {
super.onDestroy();
manager.unregisterListener(listener);
listener = null;
}
注:即便手机设备静止不动,它仍会受到地球自转带来的离心力(即重力加速度g,方向铅直向下,g=9.8m/s^2)的作用,为了消除重力加速度的作用,应使用下面代码消除:
final float alpha = 0.8f;
// Isolate the force of gravity with the low-pass filter.
gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0];
gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1];
gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2];
// Remove the gravity contribution with the high-pass filter.
linear_acceleration[0] = event.values[0] - gravity[0];
linear_acceleration[1] = event.values[1] - gravity[1];
linear_acceleration[2] = event.values[2] - gravity[2];
最终在三个坐标方向上,设备的重力加速度的数值被保存在linear_acceleration数组中。下面将补充有关手机三维坐标系的知识点:
由图可知,在手机的三维坐标系中,x、y、z轴的正方向分别是(当手机水平摆放在桌面时)平行于手机屏幕向右的方向、沿手机顶部向上的方向、垂直于手机屏幕向上的方向。
当手机在xy平面绕z轴转动时,从z轴的正方向向负方向看过去,y轴的方向是正北,角度是0°,顺时针旋转角度逐渐增大,范围是0-359°。也就是说,正东方向是z轴的值为90的时候,正南是z的值为180的时候,正西是z轴的值为270的时候。
当手机沿x轴转动时,从x轴的负方向向正方向看过去,顺时针旋转,x轴的值从0°至-180°变化,水平是0°;逆时针旋转,x轴的值从0°至180°变化。
当手机沿y轴转动时,从y轴的负方向向正方向看过去,顺时针旋转,y轴的值从0°至-90°变化,水平是0°;逆时针旋转,y轴的值从0°至90°变化。
基于上述理论可知,利用方向传感器实时获取设备在x轴和y轴方向上的角度,就可以开发出一个简单的指南针应用。
方向传感器的应用—— 指南针
代码如下所示:
private SensorManager sensorManager;
private sensorlistener listener;
private ImageView imageView;
private Sensor sensor;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView) findViewById(R.id.iv);
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
listener = new sensorlistener();
}
@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
sensorManager.registerListener(listener, sensor,
sensorManager.SENSOR_DELAY_FASTEST);
}
private class sensorlistener implements SensorEventListener {
float lastvalue = 0;
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO Auto-generated method stub
}
@Override
public void onSensorChanged(SensorEvent event) {
// 0=North, 90=East, 180=South, 270=West
float value = event.values[0];
System.out.println("夹角:" + value);
RotateAnimation animation = new RotateAnimation(-lastvalue, value,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
animation.setFillAfter(true);
imageView.startAnimation(animation);
lastvalue = value;
}
}
@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
sensorManager.unregisterListener(listener);
listener = null;
}
@Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
sensorManager.unregisterListener(listener);
listener = null;
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
sensorManager.unregisterListener(listener);
listener = null;
}
在布局中心设置了一张指南针图片,其正北指向为手机顶部,代码的重点是通过回调方法onSensorChanged(SensorEvent event),在SensorEvent.values[0]中获取由传感器返回的水平面上设备顶部与正北方向的夹角,并把该夹角作为参数初始化RotateAnimation,以实时更新图片的角度。
运行程序,下图表示当手机顶部朝向正西时,指南针指向的位置: