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)
    }
}
相关推荐
_extraordinary_1 小时前
MySQL 事务(二)
android·数据库·mysql
鸿蒙布道师5 小时前
鸿蒙NEXT开发动画案例5
android·ios·华为·harmonyos·鸿蒙系统·arkui·huawei
橙子1991101611 小时前
在 Kotlin 中什么是委托属性,简要说说其使用场景和原理
android·开发语言·kotlin
androidwork11 小时前
Kotlin Android LeakCanary内存泄漏检测实战
android·开发语言·kotlin
笨鸭先游12 小时前
Android Studio的jks文件
android·ide·android studio
gys989512 小时前
android studio开发aar插件,并用uniapp开发APP使用这个aar
android·uni-app·android studio
H3091912 小时前
vue3+dhtmlx-gantt实现甘特图展示
android·javascript·甘特图
像风一样自由12 小时前
【001】renPy android端启动流程分析
android·gitee
千里马学框架14 小时前
重学安卓14/15自由窗口freeform企业实战bug-学员作业
android·framework·bug·systrace·安卓framework开发·安卓窗口系统·自由窗口
xianrenli3819 小时前
android特许权限调试
android