问题
在 Android 服务中,我想检测屏幕旋转的任何更改。通过旋转,我不仅指纵向和横向; 我的意思是 任何 屏幕旋转的更改。此类更改示例包括:
- 纵向
- 反向纵向
- 横向
- 反向横向
请注意,本问题与设备方向的更改无关。它只涉及屏幕方向/旋转。
我尝试了什么
- 通过
ACTION_CONFIGURATION_CHANGED
监听Configuration
更改。这仅涵盖纵向和横向之间的更改,因此 180° 更改不会触发此操作。
为什么我要这样做
我正在开发一款自定义屏幕方向管理应用。
在 Android 服务中,我想检测屏幕旋转的任何更改。通过旋转,我不仅指纵向和横向; 我的意思是 任何 屏幕旋转的更改。此类更改示例包括:
请注意,本问题与设备方向的更改无关。它只涉及屏幕方向/旋转。
ACTION_CONFIGURATION_CHANGED
监听 Configuration
更改。这仅涵盖纵向和横向之间的更改,因此 180° 更改不会触发此操作。我正在开发一款自定义屏幕方向管理应用。
已批准的答案是可行的,但如果您想要更高分辨率的检测(或支持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();
}
您可以使用名为IWindowManager.watchRotation(IRotationWatcher)
的隐藏API实现此操作。从我的测试中,它似乎接受一个回调函数,每次屏幕旋转时都会调用该回调函数。该回调函数还会给出当前屏幕的旋转方向。
作为一个隐藏API,您不能直接调用它。如何使用隐藏API是一个独立的主题。当然,从可维护性的角度来看,这可能不如普通API可靠。
此外,onRotationChanged
并不在与您调用watchRotation
相同的线程中被调用。一旦进入onRotationChanged
,您可能需要将一些工作委托给其他线程,例如UI线程。
已在API 14-28中进行测试并工作正常。
以下是使其正常工作的一种方法:
android.view.IRotationWatcher
into your app. Make sure to keep it in its original package. This seems to cause the development tools to think your code has access to it while also causing the operating system to still use the real one rather than your own copy.Use reflection to call 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);
}
onRotationChanged
从未被调用过。 - MSpeed"//Stop monitoring for changes when you're done"
吗?你必须将该代码部分移动到适当的位置。(我只是为了测试而添加了注释。) - OneWorldIRotationWatcher
源代码,因为grep.com已经崩溃了:https://code.yawk.at/android/android-9.0.0_r35/android/view/IRotationWatcher.java - OneWorld另一种方法(而不是上面学院提供的方法)是在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);
}
...
}
OrientationEventListener
,但它监测的是设备的方向。正如我在问题中所说,*"这个问题不涉及设备方向的变化"*。这并不能涵盖诸如因切换应用程序、切换自动旋转或更改USER_ROTATION
而导致方向改变的情况。 - Sam