The author
Hello everyone, my name is Big Saint;
I joined the 37 Mobile Game Android team in May 2018. I used to work for Internet companies such as Aipai.
Currently, I am the head of the Android team of 37 Mobile Games in China, mainly responsible for relevant business development and some daily business coordination.
background
As for me, I was too busy with my business recently, so I didn’t have time to think and settle something. At the same time, everyone in the group can have their own thinking and summary on the business. In such an atmosphere, I can’t help but drive to start writing something on the weekend, hoping that in addition to my daily busy business, I can precipitation something and add my own growth.
As for the entry point, recently, when I was doing the function of floating ball in the application, I needed to listen to the screen rotation event to adjust the position of the floating ball. I found that in some cases, I could not receive the system callback. After thinking about it, I did a simulated monitoring of the screen rotation, which basically achieved the goal.
The problem
After the hover ball stops dragging, it needs to be attached to the left and right sides of the phone screen.
In portrait mode, the x coordinate of 0 is the left edge, and the x coordinate of the screen width is the right edge.
But in landscape, the situation is more complicated. At present, most Android phones are designed with a fringe screen. In the full screen state, the floating ball cannot receive under the fringe when it is attached to the edge, otherwise it cannot be touched.
So you need to figure out the width of the fringe and use that as the starting point for the left side of the ball so that the ball doesn’t hide under the fringe. See the figure belowBut after the screen rotates, the bangs go to the right, and the left should not be the width of the bangs as the starting point for the levitating ball. In this case, you need to listen to the rotation of the screen, with the Angle of the screen direction, you can correctly judge. Listening to the rotation of the screen simply requires rewriting the Activity’s onConfiguratuonChanged lifecycle.
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
Log.i(TAG, "on configuration changed")}Copy the code
It’s configured in the AndroidManifest
android:configChanges="orientation|screenSize"
Copy the code
When setting the Activity’s screenOrientation to sensorLandscape, the callback is not received even when the screen is rotated. Set screenOrientation to a sensor so that the screen rotates back to this position. After several attempts, you can only get the callback when switching between landscape and portrait. If you flip the landscape screen upside down, you won’t get the callback even if the landscape state stays the same and the direction is reversed.
solution
Since onConfigurationChanged does not receive callbacks, another option is to listen for screen orientation degrees
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}}}Copy the code
Determine whether the bangs are on the left or right by the degree, that is, 270 degrees on the left and 90 degrees on the right. This approach seems to solve the problem, but after a few more rotations, it turns out that there are other problems. In normal thinking, the direction of the screen should be the same as this degree, that is, the display of the screen should be top-down. But not the picture below.
At this time, the degree is 90, but the screen is displayed upside down and not rotated into the upright state. However, according to the above code, 90 degrees will be judged as the normal state of 90 degrees upright display. At this time, it is wrong to modify the position of the floating ball.
If you receive the onOrientationChanged callback to determine the direction of the screen, you can determine the direction of the screen when the degree reaches 90 degrees. If both conditions are met, the screen is rotated.
Use the following code to determine the screen display direction
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as
WindowManager
val rotation = windowManager.defaultDisplay?.rotation
The rotation to 0, 1, 2, and 3 is the four directions of the screen
Copy the code
The onOrientationChanged callback is very sensitive. If the screen is moved a little, the callback will be returned. I would like to simulate a normal screen rotation event to modify the position of the floating ball, so it can’t be refreshed very often. Here do the control, all the 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(a) {
if(mOrientationEventListener ! =null) { mOrientationEventListener!! .enable() } }fun unRegister(a) {
if(mOrientationEventListener ! =null) { mOrientationEventListener!! .disable() } }interface ScreenOrientationChangeListener {
/ * * * *@param orientation
*/
fun onChange(orientation: Int)}}Copy the code
To use, go straight 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 -> {}
}
}
})
Copy the code
If the onOrientationChanged callback is within 90 degrees, the screen is compared to surface.rotation_270, and if the onOrientationChanged callback is within 270 degrees, the screen is compared to surface.rotation_90. You can see that the Angle is increasing clockwise, and the screen direction is counting degrees counterclockwise.
Other problems
There was another problem with the method in the test. While onOrientationChanged was a sensitive callback, it also changed the degree of the screen and changed the direction of the screen. That is, it kept the screen in the same direction but increased the slope of the screen (hold the phone against the desktop and slowly stand up). When the gradient reaches a certain point, the screen will rotate and onOrientationChanged will not be called back because there is no change. This will not allow you to receive the screen rotation callback, but in the actual mobile phone scenario, this situation is relatively rare, you can try it yourself.
summary
In normal development, it is necessary to distinguish which state landscape scene is less, otherwise I think Android will give an accurate callback. Android devices are highly fragmented. In addition to bangs, there is a virtual navigation bar at the bottom edge of the screen, and the status of this navigation bar is different under different system Settings. So at this time in the suspension ball welt this demand not only to consider bangs, but also to consider the navigation bar. What’s more, the virtual navigation stays in one direction during rotation, superimposed on bangs. So the first step in figuring out the position is to listen for the rotation of the screen.