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)
    }
}
相关推荐
zh_xuan21 分钟前
Android Looper源码阅读
android
用户02738518402610 小时前
[Android]RecycleView的item用法
android
前行的小黑炭11 小时前
Android :为APK注入“脂肪”,论Android垃圾代码在安全加固中的作用
android·kotlin
帅得不敢出门12 小时前
Docker安装Ubuntu搭建Android SDK编译环境
android·ubuntu·docker
tangweiguo0305198712 小时前
Android Kotlin 动态注册 Broadcast 的完整封装方案
android·kotlin
fatiaozhang952713 小时前
浪潮CD1000-移动云电脑-RK3528芯片-2+32G-安卓9-2种开启ADB ROOT刷机教程方法
android·网络·adb·电脑·电视盒子·刷机固件·机顶盒刷机
前行的小黑炭13 小时前
Android 不同构建模式下使用不同类的例子:如何在debug模式和release模式,让其使用不同的类呢?
android·kotlin·gradle
andyguo14 小时前
AI模型测评平台工程化实战十二讲(第一讲:从手工测试到系统化的觉醒)
android
2501_9159214314 小时前
小团队如何高效完成 uni-app iOS 上架,从分工到工具组合的实战经验
android·ios·小程序·uni-app·cocoa·iphone·webview
幂简集成14 小时前
通义灵码 AI 程序员低代码 API 课程实战教程
android·人工智能·深度学习·神经网络·低代码·rxjava