更新:屏幕倒置时出现问题。请参阅查找没有插入的 Android 导航栏的位置(在服务内)
我目前正在通过我自己的研究结合https://stackoverflow.com/a/55348825/1910690研究解决方案
但我真的希望我可以只使用 Insets 或类似的东西。
===============
我尝试了一堆似乎失败的事情,因为我没有活动;如果我再四处寻找,我可能会使其与 Insets 一起使用,我仍然希望使用它们得到答案,因为它们更正式并且允许缺口等。
在此期间,我终于将各种答案拼凑在一起,得出以下结论。它可能适用于普通服务以及 AccessibilityService。
关键是在onGlobalLayout()
里面CreateLayoutHelperWnd()
你有关于屏幕大小、方向、状态栏和导航栏可见性和大小的完整信息,并且你知道你刚刚启动了你的服务或者发生了一些变化。我发现它有时会被调用两次,即使没有任何改变,可能是因为有时它会“隐藏状态+导航栏”然后“改变方向”(两者的结果相同),有时它会反过来(不同的结果(班级)。
请注意,这是我第一次认真涉足 Android 和 Java,如果做得不好,请道歉 - 欢迎评论。它也不处理错误检查(例如未找到状态栏)。另请注意,我没有在多种设备上进行过广泛的测试,我不知道它对折叠设备、多显示器甚至分屏有什么作用;但我试图解释我在各种解决方案中看到的问题(例如,在某些设备上关闭状态栏高度时有时会给出错误的答案,所以我尝试检查它是打开还是关闭)。如果需要,我会在测试更多时更新。
在 AccessibilityService 中:
// global variables
int statusBarHeight = -1;
int navigationBarHeight = -1;
int screenWidth = -1;
int screenHeight = -1;
View layoutHelperWnd;
@Override
public void onServiceConnected() {
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
CreateLayoutHelperWnd(wm);
}
public void onUnbind() {
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
DestroyLayoutHelperWnd(wm);
}
private void DestroyLayoutHelperWnd(WindowManager wm) {
wm.removeView(layoutHelperWnd);
}
private void CreateLayoutHelperWnd(WindowManager wm) {
final WindowManager.LayoutParams p = new WindowManager.LayoutParams();
p.type = Build.VERSION.SDK_INT < Build.VERSION_CODES.O ?
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY :
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
p.gravity = Gravity.RIGHT | Gravity.TOP;
p.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
p.width = WindowManager.LayoutParams.MATCH_PARENT;
p.height = WindowManager.LayoutParams.MATCH_PARENT;
p.format = PixelFormat.TRANSPARENT;
layoutHelperWnd = new View(this); //View helperWnd;
wm.addView(layoutHelperWnd, p);
final ViewTreeObserver vto = layoutHelperWnd.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
// catches orientation change and fullscreen/not fullscreen change
// read basic screen setup - not sure if needed every time, but can't hurt
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
ReadScreenDimensions(wm); // sets up screenWidth and screenHeight
statusBarHeight = GetStatusBarHeight();
navigationBarHeight = GetNavigationBarHeight();
int windowHeight = layoutHelperWnd.getHeight();
int windowWidth = layoutHelperWnd.getWidth();
Boolean isFullScreen, isStatusBar, isNavigationBar;
isFullScreen = isStatusBar = isNavigationBar = false;
Configuration config = getResources().getConfiguration();
if (config.orientation == ORIENTATION_LANDSCAPE) {
// landscape - screenWidth is for comparison to windowHeight (top to bottom), status bar may be on top, nav bar may be on right
if (screenWidth != windowHeight)
isStatusBar = true;
if (screenHeight != windowWidth)
isNavigationBar = true;
}
else {
// portrait, square, unknown - screenHeight is for comparison to windowHeight (top to bottom), status bar may be on top, nav bar may be on bottom
if (screenHeight != windowHeight) {
int difference = screenHeight - windowHeight;
if (difference == statusBarHeight)
isStatusBar = true;
else if (difference == navigationBarHeight)
isNavigationBar = true;
else {
isStatusBar = true;
isNavigationBar = true;
}
}
}
if (!isStatusBar && !isNavigationBar)
isFullScreen = true;
Log.v(TAG,"Screen change:\nScreen W,H: (" + screenWidth + ", " + screenHeight + ")\nOrientation: " + (config.orientation == ORIENTATION_LANDSCAPE ? "Landscape" : config.orientation == ORIENTATION_PORTRAIT ? "Portrait" : "Unknown") +
"\nWindow W,H: (" + windowWidth + ", " + windowHeight + ")\n" + "Status bar H: " + statusBarHeight + ", present: " + isStatusBar + "\nNavigation bar H: " + navigationBarHeight + ", present: " + isNavigationBar + "\nFullScreen: " + isFullScreen);
// do any updates required to the service's assets here
}
});
}
public void ReadScreenDimensions(WindowManager wm) {
Display myDisplay = wm.getDefaultDisplay();
Display.Mode mode = myDisplay.getMode();
screenHeight = mode.getPhysicalHeight();
screenWidth = mode.getPhysicalWidth();
}
public int GetStatusBarHeight() {
// returns 0 for no result found
int result = 0;
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = getResources().getDimensionPixelSize(resourceId);
}
return result;
}
public int GetNavigationBarHeight() {
// returns 0 for no result found
int result = 0;
int resourceId = getResources().getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
return getResources().getDimensionPixelSize(resourceId);
}
return result;
}
您还需要添加到您的清单(对于应用程序,而不是服务):
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
然后在您的主要活动调用中,然后根据返回的内容做一些有意义的事情(在某些情况下isSystemAlertPermissionGranted()
可能等到)。onActivityResult()
呃,这个我基本上是从各个地方拿来批发的,没怎么改过。:)
public static int ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE= 1234;
public boolean isSystemAlertPermissionGranted() {
// if this doesn't work, try https://stackoverflow.com/questions/46208897/android-permission-denied-for-window-type-2038-using-type-application-overlay
if (Build.VERSION.SDK_INT >= 23) {
if (!Settings.canDrawOverlays(this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE);
return false;
}
else
{
Log.v(TAG,"Permission is granted");
return true;
}
}
else { //permission is automatically granted on sdk<23 upon installation
Log.v(TAG, "Permission is granted");
return true;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE) {
// Check if the app get the permission
if (Settings.canDrawOverlays(this)) {
// Run your logic with newly-granted permission.
} else {
// Permission not granted. Change your logic accordingly.
// App can re-request permission anytime.
}
}
}