17

I need to optimize my Android app to look good on phones with a notch, like the Essential Phone.

This phones have different status bar heights than the standard 25dp value, so you can't hardcode that value.

The Android P developer preview released yesterday includes support for the notch and some APIs to query its location and bounds, but for my app I only need to be able to get the status bar height from XML, and not use a fixed value.

Unfortunately, I couldn't find any way to get that value from XML.

Is there any way?

Thank you.

fergaral
  • 2,077
  • 6
  • 17
  • 34

6 Answers6

18

I have already found the answer:

The recommended way is to use the WindowInsets API, like the following:

view.setOnApplyWindowInsetsListener { v, insets -> 
    view.height = insets.systemWindowInsetTop // status bar height
    insets
}

Accessing the status_bar_height, as it's a private resource and thus subject to change in future versions of Android, is highly discouraged.

Gabor
  • 7,352
  • 4
  • 35
  • 56
fergaral
  • 2,077
  • 6
  • 17
  • 34
8

It is not a solution, but it can explain you how it should be don in correct way and how things are going under the hood: https://www.youtube.com/watch?v=_mGDMVRO3iE

OLD SOLUTION:

Here is small trick:

@*android:dimen/status_bar_height

android studio will show you an error, but it will work like a charm :)

Andriy Antonov
  • 1,360
  • 2
  • 15
  • 29
  • 6
    Your build will probably fail on any slightly modern version of the build tools with "error: resource android:dimen/status_bar_height is private. " though. – NPike Jul 09 '18 at 17:11
  • @NPike nope. it works. but I am agreed that this definitely not the best solution... – Andriy Antonov Jul 10 '18 at 15:09
  • @AndriyAntonov no build error as such, worked well on android N,O, but didn't on P – Anup Oct 26 '18 at 13:15
  • 1
    This worked for me on one Google Pixel, and yet on another with the exact same operating system, release builds crash on it. Unfortunately it seems like this solution is unreliable and should not be used. :( – rjr-apps Jul 09 '19 at 23:17
6

Method provides the height of status bar

public int getStatusBarHeight() {
  int result = 0;
  int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
 if (resourceId > 0) {
  result = getResources().getDimensionPixelSize(resourceId);
}   
 return result;
}
Harpz
  • 183
  • 1
  • 12
  • This works well. Was working with Zoom SDK and they have declared this exact function in their SDK, just a bit different - `if(resourceId < 0) result = 25.0f * context.getResources().getDisplayMetrics().density + 0.5F;`. This will help in case resourceId isn't found like some Samsung devices. – Lalit Fauzdar Nov 10 '20 at 15:39
3

I have found a solution to work with status bar size that should be fit to any device. It is a way to transform the size from code to XML layouts.

The solution is to create a view that is auto-sized with the status bar size. It is like a guide from constraint layout.

I am sure it should be a better method but I didn't find it. And if you consider something to improve this code please let me now:

KOTLIN

class StatusBarSizeView: View {

companion object {

    // status bar saved size
    var heightSize: Int = 0
}

constructor(context: Context):
        super(context) {
    this.init()
}

constructor(context: Context, attrs: AttributeSet?):
        super(context, attrs) {
    this.init()
}

constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int):
        super(context, attrs, defStyleAttr) {
    this.init()
}

private fun init() {

    // do nothing if we already have the size
    if (heightSize != 0) {
        return
    }

    // listen to get the height
    (context as? Activity)?.window?.decorView?.setOnApplyWindowInsetsListener { _, windowInsets ->

        // get the size
        heightSize = windowInsets.systemWindowInsetTop

        // return insets
        windowInsets
    }

}

override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
    super.onSizeChanged(w, h, oldw, oldh)

    // if height is not zero height is ok
    if (h != 0 || heightSize == 0) {
        return
    }

    // apply the size
    postDelayed(Runnable {
        applyHeight(heightSize)
    }, 0)
}

private fun applyHeight(height: Int) {

    // apply the status bar height to the height of the view
    val lp = this.layoutParams
    lp.height = height
    this.layoutParams = lp
}

}

Then you can use it in XML as a guide:

<com.foo.StatusBarSizeView
        android:id="@+id/fooBarSizeView"
        android:layout_width="match_parent"
        android:layout_height="0dp" />

The "heightSize" variable is public to can use it in the code if some view need it.

Maybe it helps to someone else.

J.S.R - Silicornio
  • 1,111
  • 13
  • 16
3
class StatusBarSpacer @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
View(context, attrs) {
private var statusHeight: Int = 60

init {
    if (context is Activity && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
        context.window.decorView.setOnApplyWindowInsetsListener { _, insets ->
            statusHeight = insets.systemWindowInsetTop
            requestLayout()
            insets
        }
        context.window.decorView.requestApplyInsets()
    } else statusHeight = resources.getDimensionPixelSize(
        resources.getIdentifier("status_bar_height", "dimen", "android")
    )
}

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) =
    setMeasuredDimension(0, statusHeight)
}
aminography
  • 21,986
  • 13
  • 70
  • 74
Skotos
  • 99
  • 1
  • 1
0

I use this function to get the keyboard and statusbar insets:

ViewCompat.setOnApplyWindowInsetsListener(viewBinding.root) { _, windowInsets ->
    // get Keyboard height
    val imeHeight = windowInsets.getInsets(WindowInsetsCompat.Type.ime()).bottom
    // get statusBar height
    val statusBar = windowInsets.getInsets(WindowInsetsCompat.Type.statusBars()).top
    viewBinding.root.setPadding(0, statusBar, 0, imeHeight)
    windowInsets
}

You can also check WindowInsetsCompat.Type for other inset types

MBH
  • 16,271
  • 19
  • 99
  • 149