如何解决如何在 Android AccessibilityService 中查找状态栏、导航栏和屏幕的尺寸
我有一个 Java 应用程序 (API 23)。其中我有一个 MainActivity
和一个 AccessibilityService
。在无障碍服务中:
@Override
public void onServiceConnected() {
oView = new LinearLayout(this);
oView.setBackgroundColor(0x88ff0000); // The translucent red color
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
width,height,xPos,yPos,WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY,WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE |
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,PixelFormat.TRANSLUCENT
);
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
wm.addView(oView,params);
}
我需要调整 width
、height
、xPos
和 yPos
,使其覆盖整个屏幕,包括状态栏和导航栏。这意味着我必须找到状态栏、屏幕和导航栏的尺寸(一旦有了这些,我就可以计算出我需要的值)。
我找到了 Height of statusbar? - 最佳答案似乎是
ViewCompat.setonApplyWindowInsetsListener(rootView,new OnApplyWindowInsetsListener() {
@Override
public WindowInsetsCompat onApplyWindowInsets(View v,WindowInsetsCompat insets) {
//THIS is the value you want.
int statusBarHeight = insets.getSystemWindowInsetTop();
int navigationBar = insets.getSystemWindowInsetBottom();
// Let the view handle insets as it likes.
return ViewCompat.onApplyWindowInsets(v,insets);
}
});
但是我可以使用什么 rootView
,因为不能保证我的 Activity 会可见?我是否必须将视图的创建从 onServiceConnected()
移到 onApplyWindowInsets()
内部?
这真的是最好的方法吗?
要记住上述问题的后续问题 - 根据以上内容以及是否也回答了这些问题,我会在这里提问或在单独的问题中提问:
屏幕旋转时会发生什么?如果状态栏和/或导航栏消失(例如,如果它全屏显示)怎么办?似乎我需要处理这个问题,重新读取高度,然后重新创建视图。欢迎任何有关如何做到这一点的想法或指导。
=== 更新 - 到目前为止的进展 ===
我以为我只需要屏幕大小和状态栏信息(大小、位置),但导航栏有时位于屏幕左侧,所以我需要它,而键盘也可能会影响事情。
我可以获得屏幕尺寸
display mydisplay = wm.getDefaultdisplay();
display.Mode mode = mydisplay.getMode();
int height = mode.getPhysicalHeight();
int width = mode.getPhysicalWidth();
我仍在研究各种位置和尺寸。我尝试创建一个视图,然后阅读插图,但它给了我错误的答案。因此,除非出现更好的答案,否则我可能不得不使用
int result = 0;
int resourceId = getResources().getIdentifier("status_bar_height","dimen","android");
if (resourceId > 0) {
result = getResources().getDimensionPixelSize(resourceId);
}
但在某些情况下,如果状态栏被隐藏,并且它不会告诉我它在屏幕上的位置,这可能会给出错误的答案。我还在努力;有一些事情要尝试,我们拭目以待。
可以使用 onConfigurationChanged() 处理服务中的轮换 - 参见 How do I use a service to monitor Orientation change in Android
希望对某人有所帮助 - 请拼命寻找那些最后的作品。
解决方法
更新:屏幕倒置时出现问题。见Find placement of Android Navigation Bar without Insets (inside Service)
我目前正在结合我自己的研究和 https://stackoverflow.com/a/55348825/1910690
制定解决方案但我真的希望我可以使用 Insets 或类似的东西。
==============
我尝试了很多似乎失败的事情,因为我没有活动;如果我再多研究一些,我可能会让它与 Insets 一起工作,我仍然希望使用它们得到答案,因为它们更正式,并允许使用缺口等。
在此期间,我终于将各种答案拼凑在一起,得出以下答案。它可能适用于普通 Service 和 AccessibilityService。
重点是,在 onGlobalLayout()
内的 CreateLayoutHelperWnd()
末尾,您拥有有关屏幕尺寸、方向、状态栏和导航栏可见性和尺寸的完整信息,并且您知道刚刚开始您的服务或发生了一些变化。我发现它有时会被调用两次,即使没有任何改变,可能是因为有时它会“隐藏状态+导航栏”然后“改变方向”(两者的结果相同),有时它会反过来(不同的结果) ) 所以我将结果包装在一个类中,并将新结果与上次的结果进行比较,并且仅在结果发生变化时才执行某些操作(这就是我使用这么多全局变量的原因,因为这些变量后来移到了特殊的类)。
请注意,这是我第一次认真地涉足 Android 和 Java,如果做得不好,我深表歉意 - 欢迎评论。它也不处理错误检查(例如未找到 StatusBar)。另请注意,我没有在多个设备上进行过广泛的测试,我不知道它对折叠设备、多个显示器甚至分屏有什么作用;但我试图解释我在各种解决方案中看到的问题(例如,在某些设备上关闭状态栏高度时,有时会给出错误的答案,因此我尝试检查它是打开还是关闭)。如果需要,我会在测试更多时更新。
在无障碍服务中:
// 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),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","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.
}
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。