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

时间:2015-05-20 12:18:52

标签: android screen-orientation screen-rotation orientation-changes

问题

在Android服务中,我希望在屏幕旋转发生变化时进行检测。通过轮换,我不仅仅意味着肖像与景观;我的意思是任何更改屏幕旋转。此类更改的示例是对以下内容的更改:

  • 肖像
  • 反向肖像
  • 风景
  • 逆向风景

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

我尝试过什么

  • 通过Configuration收听ACTION_CONFIGURATION_CHANGED更改。这仅涵盖纵向和横向之间的变化,因此180°更改不会触发此操作。

为什么我这样做

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

3 个答案:

答案 0 :(得分:4)

批准的答案可行,但如果您想要更高的检测分辨率(或支持进一步支持API 3),请尝试OrientationEventListener,它可以以度为单位报告手机的方向。

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();
}

答案 1 :(得分:2)

使用隐藏的API

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

作为隐藏的API,您不能直接调用它。如何使用隐藏的API是它自己的主题。当然,从可维护性的角度来看,这可能不像普通API那样可靠。

此外,onRotationChanged未在您用于调用watchRotation的同一主题中调用。在onRotationChanged进入android.view.IRotationWatcher时,您可能希望将某些工作委托给其他线程,例如UI线程。

兼容性

在API 14 - 28中测试并使用。

实施例

这是让它发挥作用的一种方法:

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

    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);
    }
    

答案 2 :(得分:-1)

另一种方式(而不是上面提供的那个)是在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);
    }
    ...
}