SoFunction
Updated on 2025-03-02

Android realizes accurate monitoring of screen rotation in four directions

When developing a camera, I encountered a problem, which was that I needed to monitor the screen rotation. The easiest way is to use both methods: onConfigurationChanged() and OrientationEventListener to implement it, but in the end they encountered problems.

#1 At the beginning, I used the callback onConfigurationChanged() and just re-replace the method in the Activity, which is simple and convenient. After using it, I found that it can only monitor the situation of horizontal screen cutting and vertical screen. You can't listen to the left horizontal screen by cutting the right horizontal screen, and after cutting, you don't know whether it's the left horizontal screen or the right horizontal screen. The following is a simple use of listening using onConfigurationChanged().

@Override
    public void onConfigurationChanged(Configuration newConfig) {
        (newConfig);
        if( == Configuration.ORIENTATION_LANDSCAPE){
            // Horizontal screen        }else if( == Configuration.ORIENTATION_PORTRAIT){
            // Vertical screen        }
    }

#2 Later, I thought of the OrientationEventListener to monitor the real-time angle of the screen rotation. This is very flexible. The real-time angle of the mobile phone will be called back. Here is a simple implementation using OrientationEventListener. Call enable() and disable() in the appropriate location to enable and turn off listening.

class MyOrientationEventListener extends OrientationEventListener {
 
        private static final int SENSOR_ANGLE = 10;
 
        public MyOrientationEventListener(Context context) {
            super(context);
        }
 
        @Override
        public void onOrientationChanged(int orientation) {
            (TAG, "onOrientationChanged orientation=" + orientation);
            if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
                return;  //When the phone is placed flat, the valid angle cannot be detected            }
 
            //The following is the conversion of the accurate rotation angle of the mobile phone and the four direction angles (0 90 180 270)            if (orientation > 360 - SENSOR_ANGLE || orientation < SENSOR_ANGLE) {
                orientation = 0;
            } else if (orientation > 90 - SENSOR_ANGLE && orientation < 90 + SENSOR_ANGLE) {
                orientation = 90;
            } else if (orientation > 180 - SENSOR_ANGLE && orientation < 180 + SENSOR_ANGLE) {
                orientation = 180;
            } else if (orientation > 270 - SENSOR_ANGLE && orientation < 270 + SENSOR_ANGLE) {
                orientation = 270;
            } else {
                return;
            }
        }
    }
MyOrientationEventListener listener = new MyOrientationEventListener(this);
        ();
        ();

However, it is only effective when the phone is held vertically and then rotates left and right. The phone is placed flat and rotated left and right, and the angle changes cannot be sensed. The reason is that the OrientationEventListener principle only collects accelerations in the X and Y directions of Sensor for calculation. You can see from the following source code that the value of orientation is only related to X and Y. (The source code below is taken from) And there is another disadvantage to using this judgment, that is, when the screen has actually rotated and switched, but the value of the OrientationEventListener callback has not reached the value after rotation. This causes the system screen to rotate, but the UI of our app has not changed because it does not receive the callback.

class SensorEventListenerImpl implements SensorEventListener {
        private static final int _DATA_X = 0;
        private static final int _DATA_Y = 1;
        private static final int _DATA_Z = 2;
        
        public void onSensorChanged(SensorEvent event) {
            float[] values = ;
            int orientation = ORIENTATION_UNKNOWN;
            float X = -values[_DATA_X];
            float Y = -values[_DATA_Y];
            float Z = -values[_DATA_Z];        
            float magnitude = X*X + Y*Y;
            // Don't trust the angle if the magnitude is small compared to the y value
            if (magnitude * 4 >= Z*Z) {
                float OneEightyOverPi = 57.29577957855f;
                float angle = (float)Math.atan2(-Y, X) * OneEightyOverPi;
                orientation = 90 - (int)(angle);
                // normalize to 0 - 359 range
                while (orientation >= 360) {
                    orientation -= 360;
                } 
                while (orientation < 0) {
                    orientation += 360;
                }
            }
            if (mOldListener != null) {
                (Sensor.TYPE_ACCELEROMETER, );
            }
            if (orientation != mOrientation) {
                mOrientation = orientation;
                onOrientationChanged(orientation);
            }
        }

#3In order to solve the above problem, the best thing is to have a callback when the system screen rotates and tell me which angle it is currently. This is the most accurate one. But onConfigurationChanged can only tell you whether it is horizontal or vertical. Although it cannot do it, it gives a direction. When the screen rotation system calls onConfigurationChanged, you must know the angle after rotation. According to the source code, when the screen rotates, IRotationWatcher#onRotationChanged() will be called, but for the app it is a Hide API and it cannot be monitored. Then I found that the class is registered in the constructor, and the value returned by onRotationChanged() will also be saved in sRotation, so you can do a post here.

public class ScreenOrientationListener extends OrientationEventListener {
 
    private static final String TAG = ();
    private int mOrientation;
    private OnOrientationChangedListener mOnOrientationChangedListener;
    private Context mContext;
    private Field mFieldRotation;
    private Object mOLegacy;
 
    public ScreenOrientationListener(Context context) {
        super(context);
        mContext = context;
    }
 
    public void setOnOrientationChangedListener(OnOrientationChangedListener listener) {
         = listener;
    }
 
    public int getOrientation() {
        int rotation = -1;
        try {
            if (null == mFieldRotation) {
                SensorManager sensorManager = (SensorManager) (Context.SENSOR_SERVICE);
                Class clazzLegacy = ("");
                Constructor constructor = ();
                (true);
                mOLegacy = (sensorManager);
                mFieldRotation = ("sRotation");
                (true);
            }
            rotation = (mOLegacy);
        } catch (Exception e) {
            (TAG, "getRotation e=" + ());
            ();
        }
//        (TAG, "getRotation rotation=" + rotation);
 
        int orientation = -1;
        switch (rotation) {
            case Surface.ROTATION_0:
                orientation = 0;
                break;
            case Surface.ROTATION_90:
                orientation = 90;
                break;
            case Surface.ROTATION_180:
                orientation = 180;
                break;
            case Surface.ROTATION_270:
                orientation = 270;
                break;
            default:
                break;
        }
//        (TAG, "getRotation orientation=" + orientation);
        return orientation;
    }
 
    @Override
    public void onOrientationChanged(int orientation) {
        if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
            return; // When the phone is placed flat, the valid angle cannot be detected        }
        orientation = getOrientation();
        if (mOrientation != orientation) {
            mOrientation = orientation;
            if (null != mOnOrientationChangedListener) {
                (mOrientation);
                (TAG, "ScreenOrientationListener onOrientationChanged orientation=" + mOrientation);
            }
        }
    }
 
    public interface OnOrientationChangedListener {
        void onOrientationChanged(int orientation);
    }
}

The above code is to listen for real-time angle changes by listening to OrientationEventListener, and then use the reflection method to obtain the rotation in the LegacySensorManager. In this way, the angle obtained is accurate. Callback is called back when the angle changes are coordinated, which perfectly realizes monitoring when the angle rotates in 4 directions.

The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.