未经本人授权,不得转载!否则必将维权到底
最近我司产品提出了一个很常见的需求:App 通过手机光感器,获取当前的光线强度。根据光线强弱,弹窗提示用户是否切换白天/夜间模式。网上看了很多的博客,都没有解决我的问题。这个需求应该是挺常见的,故记录一下,供后人参考。
一、简介
光线感应器( Light Sensor ):光传感器主要用来检测手机周围光的强度,与其他传感器不同的是,该传感器监测手机周围光的强度,且单位为 lux。光传感器的开发与其他各种传感器的开发步骤基本相同,只是监测的是 Sensor.TYPE_LIGHT。如果学会了光感器的开发,开发其他的传感器则只需要改变监听器对象及注册监听的方法,然后根据每个人不同的业务需求,来修改后续的逻辑。
二、详细开发步骤(其他传感器基本类似)
1.通过 mContext.getSystemService() 得到传感器管理类
SensorManager mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
2.监测手机上支持的传感器,如果手机没有光传感器,一定要做好容错!!!否则你的业务逻辑会出现空指针异常(其他的传感器同理,一定要先判断手机是否拥有这个传感器)
//获取手机上支持的所有传感器
List mList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
for (Sensor sensor : mList) {
L.d("KeithXiaoY","名字:" + sensor.getName());
L.d("KeithXiaoY","type:" + sensorTypes.get(sensor.getType()) + "(" + sensor.getType() +")");
L.d("KeithXiaoY","vendor:" + sensor.getVendor());
L.d("KeithXiaoY","version:" + sensor.getVersion());
L.d("KeithXiaoY","resolution:" + sensor.getResolution());
L.d("KeithXiaoY","max range:" + sensor.getMaximumRange());
L.d("KeithXiaoY","power:" + sensor.getPower());
}
3.我的需求只需要光传感器,根据上一条打出来传感器的列表,光感器的 Type = 5,所以我做了如下判断:
//获取手机上支持的传感器
mList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
for (Sensor sensor : mList) {
if (Sensor.TYPE_LIGHT == sensor.getType()){
mIsContains = true ; //这是一个 Boolean 值,true 代表有这个传感器、false 代表没有
return;
}
}
4.下面就是要注册光传感器了
/* 第三个参数是传感器数据更新数据的速度
有以下四个值可选,他们的速度是递增的
SENSOR_DELAY_UI
SENSOR_DELAY_NORMAL
SENSOR_DELAY_GAME
SENSOR_DELAY_FASTEST*/
public void registerSensor() {
// 获得光线传感器
if ( null != mSensorManager){
Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
if ( null != sensor && mIsContains) {
mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST);
}
}
}
5.现在可以监听传感器数据的变化了,我写了一个工具类,让其实现 SensorEventListener 接口,需要重写两个方法
public class LightSensorUtils implements SensorEventListener{
// onSensorChanged() 在传感器数值发生变化已经注册监听器时调用,其更新频率就是注册中的参数三。 对于光传感器,有效数值存放在 values[0] 中的,单位为SI lunx。光传感器通常位于上方(一般靠左侧), 除了前置摄像头外还有一个孔,一般就是它。遮盖会触发 onSensorChanged()
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_LIGHT) {
L.d("KeithXiaoY",event.values[0] + "");
// 下面就是每个人自己的逻辑了,根据拿到的 event.values[0] ,来做相应的逻辑。
}
}
/* onAccuracyChanged() 会在精度改变或在注册监听器时调用。
accuracy分为4档,0(unreliable),1(low),2(medium),3(high)。注意0并不代表有问题,同时是传感器需要校准。 */
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
//这个方法一般里面什么都不写
}
}
6.上面是分步骤讲解,省略了不少代码,下面我写的光感器工具类的完整代码,注释很详细了...
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import java.util.List;
/**
* Created by KeithXiaoY on 2017/5/31.
*
* 光线传感器的类型常量是Sensor.TYPE_LIGHT。values数组只有第一个元素(values[0])有意义。表示光线的强度。最大的值是120000.0f
* Android SDK将光线强度分为不同的等级,每一个等级的最大值由一个常量表示,这些常量都定义在SensorManager类中,代码如下:
* public static final float LIGHT_SUNLIGHT_MAX =120000.0f;
* public static final float LIGHT_SUNLIGHT=110000.0f;
* public static final float LIGHT_SHADE=20000.0f;
* public static final float LIGHT_OVERCAST= 10000.0f;
* public static final float LIGHT_SUNRISE= 400.0f;
* public static final float LIGHT_CLOUDY= 100.0f;
* public static final float LIGHT_FULLMOON= 0.25f;
* public static final float LIGHT_NO_MOON= 0.001f;
* 上面的八个常量只是临界值,在实际使用光线传感器时要根据实际情况确定一个范围。
* 例如,当太阳逐渐升起时,values[0] 的值很可能会超过 LIGHT_SUNRISE,
* 当 values[0] 的值逐渐增大时,就会逐渐越过LIGHT_OVERCAST,而达到 LIGHT_SHADE。
* 当然,如果天特别好的话,也可能会达到LIGHT_SUNLIGHT,甚至更高。
*/
public class LightSensorUtils implements SensorEventListener{
private static final Object mLock = new Object();
private static LightSensorUtils instance;
private static Context mContext;
private SensorManager mSensorManager;// 传感器管理器
private List mList;
private boolean mIsContains = false;
private Boolean isBright ;//true 代表亮 false 代表暗
private final Float criticalValue = 40.0f; // 40.0f 代表人视觉的亮暗临界值
private LightSensorUtils() {
}
//需要用光感器的地方在 init 初始化
public void init(Context context) {
mContext = context;
mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
//获取手机上支持的传感器
mList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
for (Sensor sensor : mList) {
if (Sensor.TYPE_LIGHT == sensor.getType()){
mIsContains = true ;
return;
}
}
}
public static LightSensorUtils getInstance() {
if (instance == null) {
synchronized (mLock) {
if (instance == null) {
instance = new LightSensorUtils();
}
}
}
return instance;
}
/* 第三个参数是传感器数据更新数据的速度
有以下四个值可选,他们的速度是递增的
SENSOR_DELAY_UI
SENSOR_DELAY_NORMAL
SENSOR_DELAY_GAME
SENSOR_DELAY_FASTEST*/
public void registerSensor() {
// 获得光线传感器
if ( null != mSensorManager){
Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
if ( null != sensor && mIsContains) {
mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST);
}
}
}
public void unRegisterSensor() {
if ( null != mSensorManager){
mSensorManager.unregisterListener(this);
}
}
/* onSensorChanged()在传感器数值发生变化已经注册监听器时调用,其更新频率就是注册中的参数三。
对于光传感器,有效数值存放在values[0]中的,单位为SI lunx。
光传感器通常位于上方(一般靠左侧), 除了前置摄像头外还有一个孔,一般就是它。遮盖会触发onSensorChanged() */
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_LIGHT) {
L.d("KeithXiaoY",event.values[0] + "");
isBright = event.values[0] > criticalValue ? true : false ;// 40.0f 代表人视觉的亮暗临界值,我司定义 > 40.0f 为亮,<= 40.0f 为暗
// 下面就是每个人自己的逻辑了,根据拿到的 event.values[0] ,我写了一个把光的亮度分为 8 级的方法。
// getBrightString(event.values[0]);
}
}
// private int getBrightString(float value) {
// if (value >= LIGHT_SUNLIGHT_MAX) {
// currentMode = 8 ;
// return currentMode;
// } else if (value > LIGHT_SUNLIGHT) {
// currentMode = 7 ;
// return currentMode;
// } else if (value > LIGHT_SHADE) {
// currentMode = 6 ;
// return currentMode;
// } else if (value > LIGHT_OVERCAST) {
// currentMode = 5 ;
// return currentMode;
// } else if (value > LIGHT_SUNRISE) {
// currentMode = 4 ;
// return currentMode;
// } else if (value > LIGHT_CLOUDY) {
// currentMode = 3 ;
// return currentMode;
// } else if (value > LIGHT_FULLMOON) {
// currentMode = 2 ;
// return currentMode;
// } else if (value > LIGHT_NO_MOON) {
// currentMode = 1 ;
// return currentMode;
// } else {
// currentMode = -1 ;
// return currentMode;
// }
// }
/* onAccuracyChanged()会在精度改变或在注册监听器时调用。
accuracy分为4档,0(unreliable),1(low),2(medium),3(high)。
注意0并不代表有问题,同时是传感器需要校准。 */
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
public Boolean getBright() {
return isBright;
}
}
三、详细业务步骤(在 Activity 里面如何去用)
1.首先来到你监测光强度的具体页面,先初始化,这里 init() 传入的是 ApplicationContext,防止内存泄漏!!!
public class XXXActivity{
private LightSensorUtils mLightSensorUtils;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.xxx_activity);
//初始化光线传感器
mLightSensorUtils = LightSensorUtils.getInstance();
mLightSensorUtils.init(getApplicationContext());
}
}
2.由于传感器是非常耗电的,所以在 onResume() 里面注册,在 onPause() 里面解除注册,这步千万别忘记了!!!
@Override
public void onResume() {
super.onResume();
mLightSensorUtils.registerSensor();
Boolean isBright = mLightSensorUtils.getBright();
L.d("KeithXiaoY","onResume方法中的isBright : "+ isBright);
if (null != isBright){
checkTheme();
}
}
@Override
public void onPause() {
super.onPause();
mLightSensorUtils.unRegisterSensor();
}
3.这两步做完以后,就是执行大家自己的业务逻辑了,想必每个人的需求都不一样,我的具体需求就是上面 checkTheme() 方法
/*根据监听器时时返回的数据,我可以拿到当前的 isBright ,true 代表光线亮,false 代表光线暗,来弹窗提示用户是否需要切换模式 */
public void checkTheme(){
...
Boolean isBright = mLightSensorUtils.getBright();
L.d("KeithXiaoY","白天模式isBright : "+ isBright);
if (null != isBright){
if (!isBright){
SimpleDialogFragment.createBuilder(this, getSupportFragmentManager())
.setMessage("同学您好,光线不足,切换到夜间模式吧")
.setTitle(R.string.warm_tips)
.setPositiveButtonText(R.string.do_switch)
.setNegativeButtonText(R.string.do_not_switch)
.setRequestCode(REQ_SWITCH_TO_NIGHTLY).show();
}else{
......
}
}
四、结语
学会了一种传感器的使用方法后,其他传感器举一反三,相信以后再遇到传感器开发就没那么头疼了。真的写的超级详细了,应该是史上最详细吧~