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)
    }
}
相关推荐
顾林海15 分钟前
Android MMKV 深度解析:原理、实践与源码剖析
android·面试·源码阅读
雨白1 小时前
TCP/IP 核心概念详解:从网络分层到连接管理
android
Wgllss2 小时前
雷电雨效果:Kotlin+Compose+协程+Flow 实现天气UI
android·架构·android jetpack
用户207038619494 小时前
Compose 可点击文本:ClickableText Compose 中的 ClickableSpan
android
常利兵4 小时前
Kotlin作用域函数全解:run/with/apply/let/also与this/it的魔法对决
android·开发语言·kotlin
幼稚园的山代王4 小时前
Kotlin-基础语法练习一
android·开发语言·kotlin
闻不多4 小时前
用llamaindex搭建GAR遇到400
android·运维·服务器
阿华的代码王国4 小时前
【Android】适配器与外部事件的交互
android·xml·java·前端·后端·交互
跨界混迹车辆网的Android工程师5 小时前
实现Android图片手势缩放功能的完整自定义View方案,结合了多种手势交互功能
android·交互
wyjcxyyy5 小时前
打靶日记-PHPSerialize
android