11、自动亮度调节(下):手动实现简易自动亮度

上一章我们聊了光线传感器的监听机制。说白了,就是能拿到当前环境的光照值了。但拿到之后呢?

嗯,这才是关键。你得让屏幕亮度跟着动起来。

我刚开始做这个功能时,以为直接调系统亮度就完事了。结果发现,事情没那么简单。系统亮度设置和当前窗口亮度,其实是两回事。今天我们就来手动实现一个简易的自动亮度调节。

核心思路:监听 → 计算 → 设置

整个流程其实就三步:

  1. 监听光照值:通过 SensorEventListener 拿到 lux
  2. 计算目标亮度:把 lux 映射到 0~1 的亮度范围
  3. 设置窗口亮度:通过 WindowManager.LayoutParams 修改

说白了,就是一个简单的映射关系。光照强,屏幕亮;光照弱,屏幕暗。

关键点:我们修改的是当前窗口的亮度,不是系统全局亮度。这样不会影响其他应用,用户体验更好。

代码实现:简易自动亮度

先看核心代码。我习惯把逻辑封装成一个类,方便复用。

public class AutoBrightnessHelper {
    private WindowManager.LayoutParams layoutParams;
    private Activity activity;
    private SensorManager sensorManager;
    private Sensor lightSensor;

    // 亮度映射范围
    private static final float MIN_BRIGHTNESS = 0.1f;
    private static final float MAX_BRIGHTNESS = 1.0f;
    // 光照阈值(lux)
    private static final float MIN_LUX = 10f;
    private static final float MAX_LUX = 10000f;

    public AutoBrightnessHelper(Activity activity) {
        this.activity = activity;
        this.layoutParams = activity.getWindow().getAttributes();
        this.sensorManager = (SensorManager) activity.getSystemService(Context.SENSOR_SERVICE);
        this.lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
    }

    private final SensorEventListener lightListener = new SensorEventListener() {
        @Override
        public void onSensorChanged(SensorEvent event) {
            float lux = event.values[0];
            float brightness = mapLuxToBrightness(lux);
            setWindowBrightness(brightness);
        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) {
            // 一般不用处理
        }
    };

    private float mapLuxToBrightness(float lux) {
        // 限制范围
        lux = Math.max(MIN_LUX, Math.min(lux, MAX_LUX));
        // 线性映射到 0~1
        float normalized = (lux - MIN_LUX) / (MAX_LUX - MIN_LUX);
        // 映射到亮度范围
        return MIN_BRIGHTNESS + normalized * (MAX_BRIGHTNESS - MIN_BRIGHTNESS);
    }

    private void setWindowBrightness(float brightness) {
        layoutParams.screenBrightness = brightness;
        activity.getWindow().setAttributes(layoutParams);
    }

    public void start() {
        if (lightSensor != null) {
            sensorManager.registerListener(lightListener, lightSensor, SensorManager.SENSOR_DELAY_NORMAL);
        }
    }

    public void stop() {
        sensorManager.unregisterListener(lightListener);
    }
}

这段代码看着简单,但有几个坑。我一个个说。

避坑指南:我踩过的几个雷

坑一:亮度范围是 0~1,不是 0~255

我曾经直接传了个 128 进去,结果屏幕直接黑了。因为 screenBrightness 接受的是 float 类型,范围 0.0(最暗)到 1.0(最亮)。0.5 就是 50% 亮度。

坑二:别忘了在 onPause 里注销监听

如果不注销,Activity 退到后台后传感器还在跑。电量哗哗地掉。我有个项目上线后用户反馈耗电快,查了半天才发现是这个问题。

坑三:映射函数别太激进

有些同学喜欢用指数函数或分段函数,想让亮度变化更「自然」。但说实话,线性映射在大多数场景下已经够用了。过度设计反而容易出问题。

映射逻辑:光照值 → 亮度值

上面代码里的 mapLuxToBrightness 方法,其实就是一个简单的线性映射。我画了个图,方便你理解。

光照值 → 亮度值 映射关系 光照值 (lux) 10 5000 10000 亮度值 0.0 0.5 1.0 (10, 0.1) (10000, 1.0) (5000, 0.55) 线性映射 公式: brightness = 0.1 + (lux - 10) / (10000 - 10) * 0.9

你看,横轴是光照值(lux),纵轴是亮度值(0~1)。当光照从 10 lux 增加到 10000 lux 时,亮度从 0.1 平滑上升到 1.0。

这个映射不是固定的。你可以根据实际场景调整。比如在暗光环境下希望更暗,就把 MIN_BRIGHTNESS 改成 0.05。我一般会在设置里留个滑块让用户自己调。

完整使用示例

在 Activity 里怎么用?很简单:

public class MainActivity extends AppCompatActivity {
    private AutoBrightnessHelper brightnessHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        brightnessHelper = new AutoBrightnessHelper(this);
    }

    @Override
    protected void onResume() {
        super.onResume();
        brightnessHelper.start();
    }

    @Override
    protected void onPause() {
        super.onPause();
        brightnessHelper.stop();
    }
}

嗯,就这么几行。但有个细节要注意:start() 必须在 onResume 里调用,而不是 onCreate。因为 onResume 时窗口才真正可见,设置亮度才有效果。

进阶思考:为什么不用系统自动亮度?

你可能会问:系统不是自带自动亮度吗?为什么还要自己实现?

原因有几个:

  • 系统自动亮度是全局的:你改了,所有应用都受影响。有时候用户只想在当前应用里用自动亮度。
  • 系统响应慢:有些厂商的自动亮度有很长的延迟,体验不好。
  • 自定义映射:你可以根据自己的需求设计亮度曲线。比如阅读类应用可以更暗,导航类应用可以更亮。

我记得有个做电子书阅读器的朋友,他们就把 MIN_BRIGHTNESS 设到了 0.02,配合夜间模式,用户体验特别好。

小结

手动实现自动亮度,核心就是三步:监听光照、映射计算、设置窗口亮度。代码量不大,但细节不少。

我建议你动手写一遍。先跑通基础功能,再慢慢调优映射曲线。遇到问题别慌,八成是亮度范围搞错了,或者忘了注销监听。

好了,这一章就到这里。下一章我们聊聊更高级的玩法——如何让亮度变化更平滑,而不是突然跳变。


公众号:蓝海资料掘金营,微信deep3321