Android - How to use the correct posture to monitor the rotation of the Android screen

author

Hello everyone, my name is Dasheng;

I joined the 37 mobile game Android team in May 2018, and I used to work for Internet companies such as Aipai;

Currently the domestic leader of the 37 mobile game Android team, mainly responsible for related business development and some daily business coordination.

Original link: https://juejin.cn/post/7245084484756095034

background

Regarding individuals, due to the busy business some time ago, I have not had time to think and settle things; at the same time, everyone in the group can have their own thinking and conclusions on business. Under such an atmosphere, I can't help but drive to start writing on weekends.

Regarding the entry point, recently, when working on the in-app floating ball function, it is necessary to monitor the screen rotation event to adjust the position of the floating ball, and found that in some cases, the system callback cannot be received. After thinking about it, I made a screen rotation simulation monitor, which basically achieved the goal.

question

After the hoverball stops dragging, it needs to stick to the left and right sides of the phone screen.

In the vertical screen state, the x-coordinate is 0, which is the left edge, and the x-coordinate is the screen width, which is the right edge.

But in landscape mode, the situation is more complicated. Now most Android phones are designed with a notch screen. In the full screen state, when the floating ball is attached to the edge, it cannot be placed under the notch, otherwise it will not be clickable.

So at this time, it is necessary to calculate the width of the bangs, and use this width as the starting position of the left side of the floating ball, so that the floating ball will not hide under the bangs when it is attached to the edge. As shown in the figure below 92dac1d120729625b247ab3659477893.jpeg, but after the screen rotates, the notch is on the right, and the left side should not use the width of the notch as the starting point of the floating ball. In this case, you need to monitor the rotation of the screen, and you can make a correct judgment with the angle of the screen direction. To monitor the rotation of the screen, you only need to rewrite the onConfiguratuonChanged life cycle of the Activity.

override fun onConfigurationChanged(newConfig: Configuration) {
 super.onConfigurationChanged(newConfig)
 Log.i(TAG, "on configuration changed")
}

Configure in AndroidManifest

android:configChanges="orientation|screenSize"

At this time, I found a problem. When the screenOrientation of the Activity is set to sensorLandscape, the callback cannot be received even if the screen is rotated (this is a bit different from the previous understanding). So set the screenOrientation to sensor, and the screen will be called back normally when the screen is rotated. After trying several times, I found that the callback can only be received when switching between landscape and portrait.

Solutions

Since onConfigurationChanged cannot receive a callback, there is another way, which is to monitor the degree of the screen orientation, the code is as follows

mOrientationEventListener = object : OrientationEventListener(this) {
 override fun onOrientationChanged(orientation: Int) {
 Log.i(TAG, "on orientation changed angle is $orientation")
 if (orientation > 340 || orientation < 20) {
 //0
 } else if (orientation in 71..109) {
 //90
 } else if (orientation in 161..199) {
 //180
 } else if (orientation in 251..289) {
 //270
 }
 }
}

Use the degree to judge whether the bangs are on the left or the right, that is, at 270 degrees, it is on the left, and at 90 degrees, it is on the right. This method seems to solve the problem, but after a few more rotations, it is found that there are other problems. According to normal thinking, the display direction of the screen should be consistent with this degree, that is, the display of the screen should be from top to bottom. But this is not the case in the picture below.

8fb7cc795db7fea8144a4af234d2f687.jpeg

At this time, the degree is 90, but the screen is displayed upside down, and has not been rotated into an upright state. However, according to the above code, 90 degrees will be judged as the normal 90-degree upright display state. At this time, it is wrong to modify the position of the floating ball.

Then if you can judge the orientation of the screen display when you receive the callback of onOrientationChanged, that is, when the degree reaches the range of 90 degrees, you can judge the display orientation of the screen at the same time, that is, the screen is judged to be rotated when the two conditions are met at the same time.

Use the following code to determine the screen orientation

val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as
WindowManager
val rotation = windowManager.defaultDisplay?.rotation
//rotation为常量0、1、2、3,分别表示屏幕的四个⽅向

Through this judgment, the screen rotation event can be monitored accurately. The callback of onOrientationChanged is very sensitive, and it will be called back when the mobile phone screen moves a little. Then I hope to simulate the normal screen rotation event to modify the position of the floating ball, so I can't refresh it very frequently. Just do some control here, the whole code is as follows:

object ScreenOrientationHelper {
    val ORIENTATION_TYPE_0 = 0
    val ORIENTATION_TYPE_90 = 90
    val ORIENTATION_TYPE_180 = 180
    val ORIENTATION_TYPE_270 = 270
    private var mOrientationEventListener: OrientationEventListener? = null
    private var mScreenOrientationChangeListener:
            ScreenOrientationChangeListener? = null
    private var currentType = ORIENTATION_TYPE_0


    fun init(context: Context, listener: ScreenOrientationChangeListener) {
        mScreenOrientationChangeListener = listener
        mOrientationEventListener = object :
                OrientationEventListener(context) {
            override fun onOrientationChanged(orientation: Int) {
                if (mScreenOrientationChangeListener == null) {
                    return
                }
                if (orientation > 340 || orientation < 20) {
                    //0
                    if (currentType == 0) {
                        return
                    }
                    if (getScreenRotation(context) == Surface.ROTATION_0) {
                        mScreenOrientationChangeListener!!.onChange(ORIENTATION_TYPE_0)
                        currentType = ORIENTATION_TYPE_0
                    }
                } else if (orientation in 71..109) {
                    //90
                    if (currentType == 90) {
                        return
                    }
                    val angle = getScreenRotation(context)
                    if (angle == Surface.ROTATION_270) {
                        mScreenOrientationChangeListener!!.onChange(ORIENTATION_TYPE_90)
                        currentType = ORIENTATION_TYPE_90
                    }
                } else if (orientation in 161..199) {
                    //180
                    if (currentType == 180) {
                        return
                    }
                    val angle = getScreenRotation(context)
                    if (angle == Surface.ROTATION_180) {
                        mScreenOrientationChangeListener!!.onChange(ORIENTATION_TYPE_180)
                        currentType = ORIENTATION_TYPE_180
                    }
                } else if (orientation in 251..289) {
                    //270
                    if (currentType == 270) {
                        return
                    }
                    val angle = getScreenRotation(context)
                    if (angle == Surface.ROTATION_90) {
                        mScreenOrientationChangeListener!!.onChange(ORIENTATION_TYPE_270)
                        currentType = ORIENTATION_TYPE_270
                    }
                }
            }
        }
        register()
    }


    private fun getScreenRotation(context: Context): Int {
        val windowManager =
                context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
        return windowManager.defaultDisplay?.rotation ?: 0
    }


    fun register() {
        if (mOrientationEventListener != null) {
            mOrientationEventListener!!.enable()
        }
    }


    fun unRegister() {
        if (mOrientationEventListener != null) {
            mOrientationEventListener!!.disable()
        }
    }


    interface ScreenOrientationChangeListener {
        /**
         *
         * @param orientation
         */
        fun onChange(orientation: Int)
    }
}

To use it, just do it like this:

ScreenOrientationHelper.init(this, object :
ScreenOrientationHelper.ScreenOrientationChangeListener {
   override fun onChange(orientation: Int) {
       when(orientation) {
         ScreenOrientationHelper.ORIENTATION_TYPE_0 -> {}
         ScreenOrientationHelper.ORIENTATION_TYPE_90 -> {}
         ScreenOrientationHelper.ORIENTATION_TYPE_180 -> {}
         ScreenOrientationHelper.ORIENTATION_TYPE_270 -> {}
       }
   }
})

Through the above code, it is found that when the onOrientationChanged callback is within 90 degrees, it is determined that the screen display direction is compared with Surface.ROTATION_270, and when it is within 270 degrees, it is compared with Surface.ROTATION_90. It can be seen that the angle increases clockwise, while the screen orientation is calculated counterclockwise.

other problems

During the test, there is another problem with the above solution. Although the callback of onOrientationChanged is very sensitive, there are cases where the screen orientation is rotated while the degree remains unchanged. That is, keep the screen orientation unchanged, but increase the slope of the screen (put the phone sideways on the table and slowly stand it up). When the slope reaches a certain level, the screen will rotate. At this time, onOrientationChanged will not be called back because there is no change. In this way, the callback of screen rotation will not be received, but in the actual mobile phone scene, this situation is relatively rare, so you can try it yourself.

summary

In normal development, it is rare to distinguish which state is the horizontal screen, otherwise I think Android will give an accurate callback. Android devices are severely fragmented. In addition to the notch, there is also a virtual navigation bar at the lower edge of the screen. Under different system settings, the status of this navigation bar will be different if it is not displayed. So at this time, not only bangs must be considered in the requirement of floating ball welt, but also the navigation bar. What's more, during the rotation process, the virtual navigation bar will always remain in one direction, superimposed with the bangs. Then to calculate the position clearly, the first step is to monitor the rotation of the screen.

Follow me for more knowledge or contribution

07f5ed6ef9ba9da9ad64fb1501689302.jpeg

6d1b316cc96feb507a2f850217f8c046.jpeg

Guess you like

Origin blog.csdn.net/c6E5UlI1N/article/details/131335701