android 手机姿态(2)

需求

记录拍照时手机的朝向,用指南针可以解决,但有些手机会在仰角超过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)
    }
}
相关推荐
厮年断弦7 小时前
AI 辅助开发实录:一周做完一个 Android 本地音乐播放器,这些坑替你踩了
android
hai_android8 小时前
InputStage 责任链核心执行流程
android·android studio·android jetpack
mmsx10 小时前
Android 测绘计算工程化:七四参数、高程拟合与单位系统
android·kotlin
我是场12 小时前
我的NPI项目之-Touch的EE特性
android·arm开发
天空之城--12 小时前
Android一周动态:AI编程、Compose与车载生态新信号
android·flutter
恋猫de小郭14 小时前
Dart 4.0 要彻底移除 dart:mirrors,Augmentations 应该要来了
android·前端·flutter
Carson带你学Android15 小时前
Gemini 4 Argon 发布:对 Android 开发者意味着什么?
android·ai编程
事圆则缓16 小时前
Kotlin Flow、StateFlow、SharedFlow 全解析:冷流、热流与 Android 状态管理
android·kotlin·php
应用市场16 小时前
把旧 Pixel 变成相册备份中转站(上):Mac 到安卓的照片传输工具设计——流式上传、sha256 校验、adb forward 与 Bonjour
android·macos·adb·kotlin·swift
墨天梦16 小时前
B08_Fragment事务通信与双生命周期
android