我在全屏Java应用程序中遇到getDisplayCutout()
的问题。我似乎只能在onAttachedToWindow
函数中获取DisplayCutout的值。该功能完成后,我再也无法获得它。获取抠图的代码:
WindowInsets insets = myActivity.getWindow().getDecorView().getRootWindowInsets();
if (insets != null) {
DisplayCutout displayCutout = insets.getDisplayCutout();
if (displayCutout != null && displayCutout.getBoundingRects().size() > 0) {
// we have cutouts to deal with
}
}
一旦附加到视图层次结构,如何随时随地从代码中的任何位置可靠地获取显示切口?
[经过大量调查,我将其缩小为全屏应用程序的一个非常广泛的问题,令我惊讶的是,没有人问到这个问题。实际上,我们可以忽略我的应用程序,而只处理两个模板项目,您现在就可以自行制作。
[在Android Studio中,我谈论的是名为“基本活动”和“全屏活动”的电话和平板电脑项目。如果您分别创建一个,然后进行以下更改:
对于Basic,通过在Activity标签下添加android:configChanges="orientation|keyboardHidden|screenSize"
来更改清单以处理配置更改,如下所示:
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="orientation|keyboardHidden|screenSize"
android:theme="@style/AppTheme.NoActionBar">
现在同时为它们两个,将以下两个功能添加到活动文件中:
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
WindowInsets insets = getWindow().getDecorView().getRootWindowInsets();
if (insets != null) {
DisplayCutout displayCutout = insets.getDisplayCutout();
if (displayCutout != null && displayCutout.getBoundingRects().size() > 0) {
// we have cutouts to deal with
}
}
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
WindowInsets insets = getWindow().getDecorView().getRootWindowInsets();
if (insets != null) {
DisplayCutout displayCutout = insets.getDisplayCutout();
if (displayCutout != null && displayCutout.getBoundingRects().size() > 0) {
// we have cutouts to deal with
}
}
}
这是重现此问题所需要做的一切。我在API Q的Pixel 3模拟器上运行了该模拟器,在该模拟器上启用了模拟抠图并选择了BOTH(因此底部和顶部都有一个抠图)
现在,如果您在我们尝试获取显示切口(DisplayCutout displayCutout = insets.getDisplayCutout();
)的行上设置断点,则会在Basic应用程序上看到它,当您更改方向时,它可以在启动and上使用,但在全屏模式下应用程序,仅在启动时有效。
实际上,在我的应用程序中,我已经通过在onAttachedToWindow
中使用以下代码进行了测试:
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
// start a thread so that UI thread execution can continue
WorkerThreadManager.StartWork(new WorkerCallback() {
@Override
public void StartWorkSafe() {
// run the following code on the UI thread
XPlatUtil.RunOnUiThread(new SafeRunnable() {
public synchronized void RunSafe() {
WindowInsets insets = myActivity.getWindow().getDecorView().getRootWindowInsets();
if (insets != null) {
DisplayCutout displayCutout = insets.getDisplayCutout();
if (displayCutout != null && displayCutout.getBoundingRects().size() > 0) {
// we have cutouts to deal with
}
}
}
});
}
});
}
此代码启动一个线程,以便onAttachedToWindow函数可以完成运行;但线程会立即将执行发送回UI线程以检查切口。
onAttachedToWindow函数执行完成与我的代码对displayCutout进行检查之间的延迟必须在纳秒级的数量级,但是该剪切块立即不可用。
有什么想法吗?这是某种期望吗?
[方向更改时无法访问切口,我无权求助,只能记录最大的插图(纵向的顶部或底部,因为长边无法容纳),并将其应用于纵向的顶部和底部或左侧就在风景中。
这是因为我无法在android中找到一种方法来检查当前处于活动状态的风景(例如,手机的顶部在左侧还是右侧)。如果可以检查,至少可以将插图套在需要的手机边缘。
我想出了一堆可以帮助其他人的东西。首先,我将回答原始问题,然后,我将解释为什么它起作用,并为那些同样遇到相同问题的人们提供一些替代方案: app。注意:我所有的布局都是以编程方式完成的(我的布局目录中甚至没有任何xml文件)。这样做是出于跨平台的原因,也许这就是为什么我遇到这个问题而其他人则没有。
OP回答使用以下代码获取显示切口(带有适当的空检查):<activity>.getWindowManager().getDefaultDisplay().getCutout();
在我的测试中,这在onConfigurationChanged
中返回了正确的切口,并且在onAttachedToWindow
外部调用时(当然有切口)时为
not null。
解释getWindow().getDecorView().getRootWindowInsets().getDisplayCutout()
在应用程序全屏显示时,如果在onAttachedToWindow
函数外部访问,则似乎始终为NULL;我没有找到解决方法。如果您通过执行以下操作将应用从全屏模式移开:
rootView.setSystemUiVisibility(0
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
从onAttachedToWindow
外部调用getDisplayCutout会得到一个值。这当然是一个解决方案。更糟糕的是,当您在onConfigurationChanged
内部时,从函数调用中获得的值在大多数情况下似乎实际上是
错误。它既过时(显示方向更改之前的剪切位置),有时甚至完全缺少剪切(当有多个剪切时),导致safeInsets错误。有用的观察
我在尝试解决此问题时发现了一些有用的知识,这可能会帮助其他尝试处理保险开关的人。在我的方案中,我有一个全屏应用程序,不想通过尝试使用整个屏幕来增加复杂性,并且不想根据切口的大小和位置来调整布局。作为一个开发人员开发应用程序,完美地处理抠图要付出的努力远远超过我的付出。[我所希望的是能够以编程方式可靠地列出我的所有视图,而不必处理剪切点;这意味着我只需要知道可以安全放置内容的矩形的大小和位置即可。这听起来很容易,但由于上述问题而变得困难,并且在涉及系统叠加和剪切的Android信箱中出现了一些古怪之处。下面是一些如何处理结果不一的技巧。
获取可用的屏幕尺寸(全屏应用)
方法1
您可以使用来获得可用的屏幕尺寸
<activity>.getWindowManager().getDefaultDisplay().getSize()
[使用
LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
自动插入内容以避免出现切口时,无论有无切口,使用此尺寸似乎总是安全的。
尽管它似乎确实为导航栏保留了空间。设置以下标志:
已补偿了切口,这意味着您的内容可以容纳在提供的空间中,但是还将为导航栏保留额外的空间。如果您使用设备上的[[without抠图,在底部仍然为导航栏保留了空间。这不是我想要的东西。setSystemUiVisibility(0 | View.SYSTEM_UI_FLAG_LOW_PROFILE | View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION // hide nav bar | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN // hide status bar | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar | View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY // if they swipe to show the soft-navigation, hide it again after a while. );
getSize()
功能似乎以纵向方式为屏幕底部的导航栏保留空间。如果您使用的是LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
,则此大小将
方法2以像素为单位,并排显示,而忽略系统覆盖图或条形图以及电话剪裁。当与一种准确可靠的方法来确定切口插图时,这似乎是全屏应用程序的最佳方法。当用户滑动以显示状态栏和软导航时,其边缘会与应用程序边缘的空白区有些奇怪的重叠,但这是迄今为止我发现的最佳解决方案,可以避免实际包裹在应用程序边缘带有我的布局的切口。获得屏幕尺寸的另一种方法
这将获取DisplayMetrics metrics = new DisplayMetrics(); MainActivity.getWindowManager().getDefaultDisplay().getRealMetrics(metrics);
实际屏幕尺寸
使用LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
,然后使用显示切口缩小和重新放置根视图。
方法3// Get the actual screen size in pixels DisplayMetrics metrics = new DisplayMetrics(); MainActivity.getWindowManager().getDefaultDisplay().getRealMetrics(metrics); int width = metrics.widthPixels; int height = metrics.heightPixels; // if there are no cutouts, top and left offsets are zero int top = 0; int left = 0; // get any cutouts DisplayCutout displayCutout = MainActivity.getWindowManager().getDefaultDisplay().getCutout(); // check if there are any cutouts if (displayCutout != null && displayCutout.getBoundingRects().size() > 0) { // add safe insets together to shrink width and height width -= (displayCutout.getSafeInsetLeft() + displayCutout.getSafeInsetRight()); height -= (displayCutout.getSafeInsetTop() + displayCutout.getSafeInsetBottom()); // NOTE:: with LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES, we can render on the whole screen // NOTE:: and therefore we CAN and MUST set the top/left offset to avoid the cutouts. top = displayCutout.getSafeInsetTop(); left = displayCutout.getSafeInsetLeft(); }
这是我最终使用的方法,到目前为止尚未发现任何问题。
这里我们使用与
#2中相同的方法来确定可用的宽度和高度,但是使用大。这会影响我的代码计算出的高度,使我比实际上高[LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
以便Android处理该内容的位置。我以为这样做会很好,毕竟我对抠图空间中的渲染不感兴趣。但是,这会导致肖像出现奇怪的问题-android为状态栏保留的高度比顶部切口的高度greater
结论
使用<activity>.getWindowManager().getDefaultDisplay().getCutout();
获取显示切口。之后,只需确定使用哪种方法来确定要渲染的安全区域在哪里。我推荐方法2