WindowInsetsを使わないでRealSizeからSystemUIの高さを求めているとChromebookで大変なことになる


かつて、WindowInsetsが扱えるようになる前、Kitkatとかの時代ですね。ステータスバーの高さやナビゲーションバーの高さを求める方法として、Display#getRealSize()を使う方法がありました。この方法は今でもある程度通用します。

かなりやっつけコードですが、以下のように、ルートViewのgetGlobalVisibleRect()で取得したルートViewの表示領域と、displayのgetRealSize()で取得した実際のディスプレイサイズを使って、rect.topをステータスバーの高さと見なして、point.y - rect.bottom をナビゲーションバーの高さを見なすというものです。

val density = resources.displayMetrics.density
binding.root.doOnLayout {
    val rect = Rect()
    it.getGlobalVisibleRect(rect)
    val point = Point()
    windowManager.defaultDisplay.getRealSize(point)
    binding.realSize.text =
        """
        RealSize:
        StatusBar: ${rect.top / density}dp
        NavigationBar: ${(point.y - rect.bottom) / density}dp
        """.trimIndent()
}
ViewCompat.setOnApplyWindowInsetsListener(window.decorView) { _, insets ->
    window.decorView.onApplyWindowInsets(insets.toWindowInsets())
    binding.insets.text =
        """
        Insets:
        StatusBar: ${insets.systemWindowInsetTop / density}dp
        NavigationBar: ${insets.systemWindowInsetBottom / density}dp
        """.trimIndent()
    insets
}

比較用にWindowInsetsの値も表示させています。

まずはKitkatでの実行、WindowInsetsを扱えるのはLollipop以降なのでInsetsの方は表示されていませんが、StatusBar/NavigationBarの高さがとれていますね

Android 11で実行すると、WindowInsetsの値と一致しています。

ってことで、ひょっとしたらまだこういうコードが残っているアプリもあるかもしれないですね。

ではChromebookで実行してみましょう


はい、ディスプレイのRealSizeは物理ディスプレイのサイズですが、アプリ領域はウィンドウ化されているのでGlobalVisibleRectはウィンドウ内での領域情報ってことで、ディスプレイサイズからウィンドウのコンテンツ領域の高さを引いた値になるのですごく大きな値になります。

ってことで、ちゃんとWindowInsets使おうねって話でした。