需求
记录拍照时手机的朝向,用指南针可以解决,但有些手机会在仰角超过90度(即仰拍,屏幕朝下时)不能记录正确的方向。
理论
通过用手机的陀螺仪,根据加速度、磁场数据计算手机姿态,通过观察者模式通知状态变化
实现
通过陀螺仪获取://方位角、俯仰角与翻滚角,通过计算获取手机姿态,包括以下:屏幕方向:竖屏、横屏(右上)、横屏(右下),是否仰拍:俯拍、仰拍。
代码
MySensorViewModel用于记录角度,通知观察者
Kotlin
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
object MySensorViewModel : ViewModel() {
//竖屏角度
var currentDegreePortrait = MutableLiveData(0f)
//横屏角度
var currentDegreeLandscape = MutableLiveData(0f)
}
主要代码,用于监控设备姿态变化,计算后更新ViewModel
Kotlin
import android.content.Context
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
/**
* @ProjectName: mydemo
* @Package: esa.library.sensor
* @ClassName: MySensor
* @Description: java类作用描述
* @Author: Administrator
* @CreateDate: 2023/04/06 13:33
* @UpdateUser: Administrator
* @UpdateDate: 2023/04/06 13:33
* @UpdateRemark: 更新说明
* @Version: 1.0
*/
class MySensor{
companion object {
private var instance: MySensor? = null
@Synchronized
fun getInstance(): MySensor {
try {
if (instance == null) {
instance = MySensor()
}
} catch (ex: Exception) {
instance = MySensor()
}
return instance!!
}
}
//传感器管理
private lateinit var sensorManager: SensorManager
//传感器
private lateinit var sensor: Sensor
/**
* @param context
* @return void
* @description 初始化
* @author Administrator
* @time 2023/04/06 13:42
*/
fun init(context: Context) {
sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
val ll = Sensoreventlistener()
sensorManager.registerListener(ll, null, SensorManager.SENSOR_DELAY_NORMAL)
//注册加速度传感器监听
val acceleSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
sensorManager.registerListener(ll, acceleSensor, SensorManager.SENSOR_DELAY_NORMAL)
//注册磁场传感器监听
val magSensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)
sensorManager.registerListener(ll, magSensor, SensorManager.SENSOR_DELAY_NORMAL)
}
//加速度参数与磁场参数
private var gravity: FloatArray? = null
private var geomagnetic: FloatArray? = null
//上一次刷新时间
private var lastrefresh = System.currentTimeMillis()
//刷新频率
private val tik: Long = 500
//方位角、俯仰角与翻滚角
var values = FloatArray(3)
//传感器监听类
private inner class Sensoreventlistener : SensorEventListener {
override fun onSensorChanged(sensorEvent: SensorEvent) {
if (System.currentTimeMillis() - lastrefresh > tik) {
lastrefresh = System.currentTimeMillis()
try {
when (sensorEvent.sensor.type) {
Sensor.TYPE_ACCELEROMETER -> gravity = sensorEvent.values.clone()
Sensor.TYPE_MAGNETIC_FIELD -> geomagnetic = sensorEvent.values.clone()
}
//计算-方位角、俯仰角与翻滚角
if (gravity != null && geomagnetic != null) {
val R = FloatArray(9)
if (SensorManager.getRotationMatrix(R, null, gravity, geomagnetic)) {
SensorManager.getOrientation(R, values)
updateView()
}
}
} catch (ex: Exception) {
println(ex.toString())
}
}
}
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {}
}
var currentDegreePortrait = 0f
var currentDegreeLandscape = 0f //横屏角度
/**
* @param
* @return void
* @description 更新数据
* @author Administrator
* @time 2023/04/04 16:52
*/
private fun updateView() {
//横屏
currentDegreeLandscape = ((360f + values[0] * 180f / Math.PI) % 360).toFloat()
//右上横屏
currentDegreeLandscape = (currentDegreeLandscape + 90) % 360
//右下横屏
if (values[2] > 0) {
currentDegreeLandscape = (currentDegreeLandscape + 180) % 360
}
//竖屏
currentDegreePortrait = ((360f + values[0] * 180f / Math.PI) % 360).toFloat()
//判断是否有翻转
//取绝对值
val v2 = if (values[2] < 0) -values[2] else values[2]
if (v2 > Math.PI / 2) {
currentDegreePortrait = (currentDegreePortrait + 180) % 360
}
// //被观察者的通知更新
MySensorViewModel.currentDegreePortrait.postValue(currentDegreePortrait)
MySensorViewModel.currentDegreeLandscape.postValue(currentDegreeLandscape)
}
}