如何全局检测屏幕旋转何时发生变化?
How can I globally detect when the screen rotation changes?
问题
在 Android 服务中,我想在屏幕旋转发生变化时进行检测。通过旋转,我不只是指肖像与风景。我的意思是 any 改变屏幕旋转。此类更改的示例是更改为:
- 纵向
- 反转纵向
- 横向
- 反转风景
请注意,此问题与 设备 方向的更改无关。只是关于屏幕 orientation/rotation.
我试过的
- 监听
Configuration
通过 ACTION_CONFIGURATION_CHANGED
更改。这仅涵盖纵向和横向之间的变化,因此 180° 变化不会触发此。
我为什么要这样做
我正在开发自定义屏幕方向管理应用。
批准的答案会起作用,但如果您想要更高分辨率的检测(或支持进一步返回 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 期间测试并工作。
例子
这是让它工作的一种方法:
- 将
android.view.IRotationWatcher
复制到您的应用中。确保将其保存在原包装中。这似乎导致开发工具认为您的代码可以访问它,同时还导致操作系统仍然使用真实的而不是您自己的副本。
使用反射调用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);
}
问题
在 Android 服务中,我想在屏幕旋转发生变化时进行检测。通过旋转,我不只是指肖像与风景。我的意思是 any 改变屏幕旋转。此类更改的示例是更改为:
- 纵向
- 反转纵向
- 横向
- 反转风景
请注意,此问题与 设备 方向的更改无关。只是关于屏幕 orientation/rotation.
我试过的
- 监听
Configuration
通过ACTION_CONFIGURATION_CHANGED
更改。这仅涵盖纵向和横向之间的变化,因此 180° 变化不会触发此。
我为什么要这样做
我正在开发自定义屏幕方向管理应用。
批准的答案会起作用,但如果您想要更高分辨率的检测(或支持进一步返回 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 期间测试并工作。
例子
这是让它工作的一种方法:
- 将
android.view.IRotationWatcher
复制到您的应用中。确保将其保存在原包装中。这似乎导致开发工具认为您的代码可以访问它,同时还导致操作系统仍然使用真实的而不是您自己的副本。 使用反射调用
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); }