【Android】获取屏幕方向的几种方式和最优解

在这里插入图片描述

1.前言

2.正文

有时我们需要获取屏幕的方向去加载布局, 但是方向不是一成不变的, 如果没有固定screenOritention, 那么就需要我们要知道当前的方向是什么, 特别是我们对DecorView做操作的时候

下面, 列举三个方式去获取屏幕的方向

方式一: 通过系统配置的方式(有缺陷)

    //是否为竖屏(系统配置)
    @JvmStatic
    fun isOrientationPortrait_ofSysConfig(): Boolean =
       Resources.getSystem().configuration.orientation == Configuration.Orientation.PORTRAIT

方式二: 通过显示的旋转角的方式(准确)

    //是否为竖屏(更为准确)
    @JvmStatic
    fun isOrientationPortrait_ofDefDisplay(context: Context): Boolean =
        getDefOrientation(context) == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT

    @JvmStatic
    fun getDefOrientation(context: Context): Int =
        when (getDefRotation(context)) {
            Surface.ROTATION_90, Surface.ROTATION_270 -> ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
            else -> ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
        }
        
    //获取旋转
    @JvmStatic
    fun getDefRotation(context: Context): Int =
        (getSystemService(context, CContext.WINDOW_SERVICE) as WindowManager).defaultDisplay.rotation

方式三: 通过宽高的方式(联合方式二判断)

为什么会通过这种呢, 因为在某些平板设备中, 会带有桌面模式, 例如三星的dex模式, 还有小米的桌面模式, 这时候用户是可以调节窗口的大小的, 但是通过方式二, 还是判断为竖屏, 但是窗口已经被拉伸到类似横屏的样式了

    //是否为竖屏(通过宽高的方式)
    @JvmStatic
    fun isOrientationPortrait_ofSysMetrics(): Boolean =
        getSysHeightPixels() >= getSysWidthPixels()

    @JvmStatic
    fun getSysHeightPixels(): Int =
        Resources.getSystem().displayMetrics.heightPixels

    @JvmStatic
    fun getSysWidthPixels(): Int =
        Resources.getSystem().displayMetrics.widthPixels

方式四: 通过传感器的方式(有局限)

这种方式比较适合相机开发, 不仅可以判断横竖屏, 还可以知道当前已经旋转到哪个角度了, 结合Lifecycle, 我们写一个角度变化监听的代理类, 并且可以自动disable

@OptInApiInit_ByLazy
@OptInApiCall_BindLifecycle
class ScreenOrientationOfSensorProxy<A>(private val _activity: A) : BaseWakeBefDestroyLifecycleObserver() where A : Activity, A : LifecycleOwner {
    companion object {
        private val SENSOR_ANGLE = 10
    }

    interface IScreenOrientationChangedListener {
        fun onOrientationChanged(degree: Int){}
        fun onDegreeChanged(degree: Int){}
    }
    

    private var _orientationEventListener: OrientationEventListener? = null
    private var _screenOrientationChangedListener: IScreenOrientationChangedListener? = null

    

    init {
        _activity.runOnMainThread(::startObserveScreen)
    }
    

    fun setScreenOrientationChangedListener(listener: IScreenOrientationChangedListener) {
        _screenOrientationChangedListener = listener
    }

    

    override fun onDestroy(owner: LifecycleOwner) {
        _screenOrientationChangedListener = null
        disableOrientationListener()
        super.onDestroy(owner)
    }

    

    private fun startObserveScreen() {
        disableOrientationListener()
        Log.d(TAG, "startObserveScreen: ")
        _orientationEventListener = object : OrientationEventListener(_activity, CSensorManager.SENSOR_DELAY_NORMAL) {
            override fun onOrientationChanged(orientation: Int) {
//                    Log.v(TAG, "startObserveScreen: onOrientationChanged " + orientation);
                if (orientation == ORIENTATION_UNKNOWN) return  //手机平放时,检测不到有效的角度
                _screenOrientationChangedListener?.onDegreeChanged(orientation)

                if (orientation > 360 - SENSOR_ANGLE || orientation < SENSOR_ANGLE) {//下面是手机旋转准确角度与四个方向角度(0 90 180 270)的转换
                    _screenOrientationChangedListener?.onOrientationChanged(0)
                } else if (orientation > 90 - SENSOR_ANGLE && orientation < 90 + SENSOR_ANGLE) {
                    _screenOrientationChangedListener?.onOrientationChanged(90)
                } else if (orientation > 180 - SENSOR_ANGLE && orientation < 180 + SENSOR_ANGLE) {
                    _screenOrientationChangedListener?.onOrientationChanged(180)
                } else if (orientation > 270 - SENSOR_ANGLE && orientation < 270 + SENSOR_ANGLE) {
                    _screenOrientationChangedListener?.onOrientationChanged(270)
                }
            }
        }
        if (_orientationEventListener!!.canDetectOrientation()) {
            Log.v(TAG, "startObserveScreen: Can detect orientation")
            _orientationEventListener!!.enable()
        } else {
            Log.v(TAG, "startObserveScreen: Cannot detect orientation")
            disableOrientationListener()
        }
    }

    private fun disableOrientationListener() {
        Log.d(TAG, "disableOrientationListener: ")
        if (_orientationEventListener != null) {
            _orientationEventListener!!.disable()
            _orientationEventListener = null
        }
    }
}

任何去使用

class ElemKAndroidViewActivity : AppCompatActivity() {
    @OptIn(OptInApiInit_ByLazy::class, OptInApiCall_BindLifecycle::class)
    private val _screenOrientationOfSensorProxy: ScreenOrientationOfSensorProxy<ElemKAndroidViewActivity> by lazy { ScreenOrientationOfSensorProxy(this) }

    @OptIn(OptInApiInit_ByLazy::class, OptInApiCall_BindLifecycle::class)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        _screenOrientationOfSensorProxy.apply {
            bindLifecycle(this@ElemKAndroidViewActivity)
            setScreenOrientationChangedListener(object : ScreenOrientationOfSensorProxy.IScreenOrientationChangedListener {
                override fun onDegreeChanged(degree: Int) {
                    //角度改变(更加灵活)
                }

                override fun onOrientationChanged(degree: Int) {
                    //角度改变(0,90,180,270)
                }
            })
        }
    }
}

相关代码, 可以查看这个开源项目
https://github.com/mozhimen/SwiftKit/blob/master/basick/src/main/java/com/mozhimen/basick/elemk/android/view/ScreenOrientationOfSensorProxy.kt

总结

如果你希望获取最全面的横竖屏信息, 你可以 方式二 or 方式三, 但是如果你希望获取最全面的传感器角度信息, 那么方式四也加上, 那么为什么不全用方式四呢, 因为你平放手机, 手机传感器就无法通过加速度计算除角度, 从而获取不到了.