You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 08:57:05