创建 Activity 时如何获取 DisplayCutout 高度?

Posted

技术标签:

【中文标题】创建 Activity 时如何获取 DisplayCutout 高度?【英文标题】:How to get DisplayCutout height when creating an Activity? 【发布时间】:2019-02-10 20:20:06 【问题描述】:

我有一个全屏游戏SurfaceView(纵向)和在表面上连续渲染的线程。

SurfaceView 初始化为onCreate,其大小由屏幕的宽度和高度(以像素为单位)决定。

创建后,设置为 Game Activity 的内容视图(无 XML 定义):

Bitmap frameBuffer = Bitmap.createBitmap(screenWidth, screenHeight, Config.ARGB_8888);

MySurfaceView renderView = new MySurfaceView(this, frameBuffer);

this.setContentView(renderView);

问题是,屏幕高度不包括 Display Cutouts(对于具有它们的设备)并且高于允许的绘图屏幕,因为 renderView 放置在 cutout 区域下方并且不使用它进行渲染。

我试图找到真正的screen height - display cutout height,以便我可以使帧缓冲区具有那个高度。

你知道是否有办法获得显示剪切区域高度onCreate,就像我们能够获得导航/状态栏高度一样。下面是一个返回导航栏高度的sn-p:

Resources resources = getResources();
int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) 
    int navigationBarHeight = resources.getDimensionPixelSize(resourceId);

感谢您的帮助!

--- 更新 ---

我会尽量澄清这个问题:

1)onCreate创建了一个全屏纵向SurfaceView,具有屏幕宽度和高度的尺寸(无XML)

2) API 级别 > 19 (KitKat) 启用沉浸式模式

3) 屏幕宽度和高度由 Display.getSize() 或 Display.getRealSize() 获取,具体取决于 API 级别

4) 在具有顶部显示切口的设备上,视图会自动放置在切口下方,底部内容会被切掉,这就是问题

5) 我想得到 Display Cutout onCreate 的高度,这样我就可以将 SurfaceView 的高度创建为 screenHeight - displayCutoutHeight

6) 问题归结为找到 Display Cutout 的高度。

【问题讨论】:

【参考方案1】:

WindowInsets.getDisplayCutout() 返回 DisplayCutout 对象(如果存在)。

要获得显示切口的边界矩形,您可以使用

DisplayCutout.getBoundingRects()

它将为您提供屏幕上所有显示切口(非功能区域)的Rect 列表。

为了得到每个显示切口的高度,

Rect.height()

这也可能返回负值,因此请确保采用高度模数。

有关显示切口支持的更多信息,请参阅以下链接:

android:windowLayoutInDisplayCutoutMode

DisplayCutout

layoutInDisplayCutoutMode

WindowInsets

WindowInsets.getDisplayCutout()

【讨论】:

感谢您的回答。我读到我可以使用 WindowInsets 获得它,但我不明白如何在 onCreate 期间获得插图。我已经阅读了您发布的所有链接,所有示例都显示了如何使用视图对象上的侦听器来做到这一点:onApplyWindowInsetsListener。问题是视图放置在插图(切口)下方,并且侦听器始终为切口返回零值,因为它们没有与视图重叠 - 它们在另一个窗口中位于视图上方。您能否提供一个代码 sn-p 您将如何获得 insets 对象?从那里我可以得到 Rect 值。【参考方案2】:

似乎没有办法在 onCreate 中获取WindowInsets 对象。您的Activity 必须先附加到窗口。所以你的选择是:

1.

override fun onAttachedToWindow() 
    val cutout = window.decorView.rootWindowInsets.displayCutout

2.

override fun onCreate(savedInstanceState: Bundle?) 
    super.onCreate(savedInstanceState)
    contentView?.setOnApplyWindowInsetsListener  _, insets ->
        val cutout = insets.displayCutout
        return@setOnApplyWindowInsetsListener insets
    

【讨论】:

【参考方案3】:

在 onCreate() 中是这样的:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) 

    drumRoll1Button.addOnLayoutChangeListener(new View.OnLayoutChangeListener() 
        @Override
        public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) 

                DisplayCutout displayCutout = drumRoll1Button.getRootWindowInsets().getDisplayCutout();
                if (displayCutout!=null) 
                    displayCutoutLengthPx = displayCutout.getSafeInsetLeft();
                
            Log.v(LOG_TAG,"displayCutoutLengthPx:"+displayCutoutLengthPx);
        
    );

如果您处于原始问题所要求的纵向模式,请在屏幕上的任何视图(对于drumRoll1Button)和getSafeInsetTop()(对于getSafeInsetLeft())进行交换。此示例适用于景观。

【讨论】:

【参考方案4】:

Roman 回答的修改(上图)对我有用:

    @Override
    public void onAttachedToWindow() 
        super.onAttachedToWindow();
        Log.i("Chicken", "cutout: " + getWindow().getDecorView().getRootWindowInsets().getDisplayCutout());
   

【讨论】:

请注意,如果您的手机在此方法中旋转过程中稍微旋转了某个角度,您会收到错误提示

以上是关于创建 Activity 时如何获取 DisplayCutout 高度?的主要内容,如果未能解决你的问题,请参考以下文章

Android View如何获取焦点

从 Activity 返回时如何获取最后一个 ListView 状态

在 Activity 重新创建时如何确定 Fragment 恢复?

Android不依赖Activity的全局悬浮窗实现

Test for Activity to display Deslayed

如何在activity中获取fragment的控件,然后修改控件的内容,