如何全局检测屏幕旋转何时发生变化?

How can I globally detect when the screen rotation changes?

问题

在 Android 服务中,我想在屏幕旋转发生变化时进行检测。通过旋转,我不只是指肖像与风景。我的意思是 any 改变屏幕旋转。此类更改的示例是更改为:

请注意,此问题与 设备 方向的更改无关。只是关于屏幕 orientation/rotation.

我试过的

我为什么要这样做

我正在开发自定义屏幕方向管理应用。

批准的答案会起作用,但如果您想要更高分辨率的检测(或支持进一步返回 API 3),请尝试 OrientationEventListener,它可以报告 phone 以度为单位。

mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);

OrientationEventListener orientationEventListener = new OrientationEventListener(this,
        SensorManager.SENSOR_DELAY_NORMAL) {
    @Override
    public void onOrientationChanged(int orientation) {
        Display display = mWindowManager.getDefaultDisplay();
        int rotation = display.getRotation();
        if(rotation != mLastRotation){
             //rotation changed
             if (rotation == Surface.ROTATION_90){} // check rotations here
             if (rotation == Surface.ROTATION_270){} //
        }
        mLastRotation = rotation;
    }
};

if (orientationEventListener.canDetectOrientation()) {
    orientationEventListener.enable();
}

另一种方法(而不是上面学院提供的方法)是在onCreate

中手动检查旋转角度
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ...
    Display display = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
    int rotation = display.getRotation();
    if (rotation == Surface.ROTATION_180) { // reverse portrait
        setContentView(R.layout.main_reverse_portrait);
    } else {  // for all other orientations
        setContentView(R.layout.main);
    }
    ...
}

使用隐藏的API

您可以使用名为 IWindowManager.watchRotation(IRotationWatcher) 的隐藏 API 来执行此操作。从我的测试来看,每次屏幕旋转发生变化时,它似乎都会调用一个回调。回调似乎也给出了当前屏幕旋转。

作为一个隐藏的API,你不能直接调用它。如何使用隐藏的 APIs 是它自己的话题。而且,当然,从可维护性的角度来看,这可能不如正常 APIs.

可靠。

此外,onRotationChanged isn't called in the same thread you used to call watchRotation. You'll probably want to delegate some work to a different thread such as the UI thread once you're in onRotationChanged

兼容性

在 API 14 - 28 期间测试并工作。

例子

这是让它工作的一种方法:

  1. android.view.IRotationWatcher 复制到您的应用中。确保将其保存在原包装中。这似乎导致开发工具认为您的代码可以访问它,同时还导致操作系统仍然使用真实的而不是您自己的副本。
  2. 使用反射调用watchRotation:

    try {
        Class<?> serviceManager = Class.forName("android.os.ServiceManager");
        IBinder serviceBinder = (IBinder)serviceManager.getMethod("getService", String.class).invoke(serviceManager, "window");
        Class<?> stub = Class.forName("android.view.IWindowManager$Stub");
        Object windowManagerService = stub.getMethod("asInterface", IBinder.class).invoke(stub, serviceBinder);
        Method watchRotation;
        if (Build.VERSION.SDK_INT >= 26)
            watchRotation = windowManagerService.getClass().getMethod("watchRotation", IRotationWatcher.class, int.class);
        else
            watchRotation = windowManagerService.getClass().getMethod("watchRotation", IRotationWatcher.class);
    
        //This method seems to have been introduced in Android 4.3, so don't expect to always find it
        Method removeRotationWatcher = null;
        try {
            removeRotationWatcher = windowManagerService.getClass().getMethod("removeRotationWatcher", IRotationWatcher.class);
        }
        catch (NoSuchMethodException ignored) {}
    
        IRotationWatcher.Stub screenRotationChanged = new IRotationWatcher.Stub() {
            @Override
            public void onRotationChanged(int rotation) throws RemoteException {
                //Do what you want here
                //WARNING: This isn't called in the same thread you were in when you called watchRotation
            }
        };
    
        //Start monitoring for changes
        if (Build.VERSION.SDK_INT >= 26)
            watchRotation.invoke(windowManagerService, screenRotationChanged, Display.DEFAULT_DISPLAY);
        else
            watchRotation.invoke(windowManagerService, screenRotationChanged);
    
        //Stop monitoring for changes when you're done
        if (removeRotationWatcher != null) {
            removeRotationWatcher.invoke(windowManagerService, screenRotationChanged);
    } catch (ClassNotFoundException | ClassCastException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
        throw new RuntimeException(e);
    }