【Android】获取屏幕方向的几种方式和最优解
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 方式三, 但是如果你希望获取最全面的传感器角度信息, 那么方式四也加上, 那么为什么不全用方式四呢, 因为你平放手机, 手机传感器就无法通过加速度计算除角度, 从而获取不到了.