CameraX拍照Bitmap加水印时SensorEvent返回空、方位角俯仰角无响应问题
问题根因
你代码中直接将mSensorEvent初始化为null,且没有注册设备方向传感器的监听来更新该变量,所以在生成水印时读取三个角度值自然全部为null。同时不建议直接持有SensorEvent实例,系统会复用该对象,存储的数值后续会出现错乱。
修复步骤
1. 先声明所需全局变量
// 传感器相关 private lateinit var sensorManager: SensorManager private var rotationVectorSensor: Sensor? = null // 存储最新的三个角度值,避免持有SensorEvent private var latestAzimuth: Float? = null private var latestPitch: Float? = null private var latestRoll: Float? = null // 传感器计算用的临时数组 private val rotationMatrix = FloatArray(9) private val orientationValues = FloatArray(3)
2. 注册传感器监听获取实时角度
在Activity的onCreate方法中初始化传感器,在onResume中注册监听:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 初始化传感器管理器 sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager // 使用旋转矢量传感器,比单独用加速度+磁场传感器更稳定,不需要额外权限 rotationVectorSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR) } override fun onResume() { super.onResume() // 注册传感器监听,采样率可以根据需求调整,SENSOR_DELAY_NORMAL适合普通拍照场景 rotationVectorSensor?.let { sensorManager.registerListener(sensorListener, it, SensorManager.SENSOR_DELAY_NORMAL) } } // 传感器事件监听 private val sensorListener = object : SensorEventListener { override fun onSensorChanged(event: SensorEvent) { if (event.sensor.type == Sensor.TYPE_ROTATION_VECTOR) { // 从旋转矢量计算方向矩阵 SensorManager.getRotationMatrixFromVector(rotationMatrix, event.values) // 计算方位角、俯仰角、横滚角 SensorManager.getOrientation(rotationMatrix, orientationValues) // 弧度转角度,存入全局变量 latestAzimuth = Math.toDegrees(orientationValues[0].toDouble()).toFloat() latestPitch = Math.toDegrees(orientationValues[1].toDouble()).toFloat() latestRoll = Math.toDegrees(orientationValues[2].toDouble()).toFloat() } } override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) { // 不需要额外处理可以留空 } } // onPause时注销监听,避免后台耗电 override fun onPause() { super.onPause() sensorManager.unregisterListener(sensorListener) }
3. 修改onImageSaved中的取值逻辑
override fun onImageSaved(output: ImageCapture.OutputFileResults) { // 取值时添加默认值兜底,避免出现null val azimuthWMText = resources.getString(R.string.value_format_2, latestAzimuth ?: 0f) val pitchWMText = resources.getString(R.string.value_format_2, latestPitch ?: 0f) val rollWMText = resources.getString(R.string.value_format_2, latestRoll ?: 0f) // 后续水印逻辑保持不变即可 ... }
其他优化建议
- 你当前水印的Y坐标是写死的固定值,不同分辨率的手机上会出现水印位置错乱,建议用
textSize + 行间距动态计算每一行的Y坐标,不要针对不同系统版本写死数值。 - 保存Bitmap后记得调用
originalBitmap.recycle()回收内存,避免OOM。
内容的提问来源于stack exchange,提问作者Georgi Tasev
相关产品推荐
相关产品推荐

