80

Is there a way to get the height of the statusbar + titlebar? Checking the dev forum shows the same question but no solution (that I could find).

I know we can get it after the initial layout pass, but I'm looking to get it in onCreate() of my activity.

Thanks

user291701
  • 38,411
  • 72
  • 187
  • 285
  • hi, i was wondering why do you want to get that height? – Jorgesys Jul 28 '10 at 17:33
  • I need to set the size of a webview hosted in this activity to whatever the available height is (minus titlebar/statusbar) Thanks – user291701 Jul 28 '10 at 17:36
  • The material.io give the answer. > Metrics: > Android status bar height: 24dp > Chrome window height: 32dp https://material.io/guidelines/layout/structure.html#structure-system-bars – cong Apr 21 '18 at 01:52

14 Answers14

116
Rect rectgle= new Rect();
Window window= getWindow();
window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
int StatusBarHeight= rectgle.top;
int contentViewTop= 
    window.findViewById(Window.ID_ANDROID_CONTENT).getTop();
int TitleBarHeight= contentViewTop - StatusBarHeight;

   Log.i("*** Jorgesys :: ", "StatusBar Height= " + StatusBarHeight + " , TitleBar Height = " + TitleBarHeight); 

Get the Height of the status bar on the onCreate() method of your Activity, use this method:

public int getStatusBarHeight() { 
      int result = 0;
      int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
      if (resourceId > 0) {
          result = getResources().getDimensionPixelSize(resourceId);
      } 
      return result;
} 
Jorgesys
  • 124,308
  • 23
  • 334
  • 268
  • 11
    This makes a significant assumption that the status bar is at the top. I would expect that to always be the case. – hackbod Jul 29 '10 at 02:20
  • you should call it in delayed callback or after onCreate method where sizes will be already measured. – Roger Alien Jan 23 '12 at 23:51
  • As @hackbod mentioned this assumes that the status bar is at the top, and if we consider Android tablets system bar at the bottom this fails (but could be easily adapter to screen height - window bottom. – Tom Feb 15 '12 at 00:16
  • 1
    @Tom: My experience is that when the system bar (nav or combined) is at the bottom, this amount gets chopped out of the total height of the display reported by DisplayMetrics, so that your app is made to think that it has a smaller available space for drawing. This is in an app that targets API 8, so if you target something higher (e.g., Honeycomb, where Nav or combined bars first started getting used) YMMV. If the reason that you need the height of the status bar is to deduct it from the available space, then you might not need to do that deduction for the NAV or Combined bar at the bottom. – Carl Dec 10 '12 at 20:25
  • 1
    Can someone please indicate the xml counterpart as well? Kinda like the following for actionbar size: `"?attr/actionBarSize"` – Pier Betos Apr 01 '15 at 03:44
47

For those, like me, who want to use it in your XML layouts:

<...
  android:layout_marginTop="@*android:dimen/status_bar_height"
  ... />

Don't be confused by that Android Studio (2.2 Preview 3 -- at the moment of writing) doesn't support this notion. The runtime does. I took it from the Google's source code.

Yazon2006
  • 3,684
  • 3
  • 25
  • 36
Aleksey Gureiev
  • 1,729
  • 15
  • 17
  • 14
    Error:(30, 43) Resource is not public. (at 'layout_marginTop' with value '@android:dimen/status_bar_height'). – User Jul 14 '16 at 09:58
  • 5
    @khan Don't forget the star(*). It's @*android:dimen/status_bar_height – Kabir Jul 27 '16 at 13:23
  • 8
    please, can you explain what means @*android notation. thanks – GPack Oct 11 '16 at 10:53
  • I thought this was working until it failed some tests. For me it broke on devices devices running ~ 5.0.1. The margin on these versions was massive and pushed elements off the screen. – masterwok Oct 17 '16 at 22:15
  • 1
    Unlike jtsan, in my case this worked well all the way back to API 15. – iSWORD Dec 04 '16 at 14:41
  • 3
    Working well until api 24, crashes on api 25 (android 7.1.1). It will give you the error `You must supply a layout_height(width) attribute` if you use it on layout files. – ntcho Dec 24 '16 at 14:21
  • 1
    @BedrockDev For me it works on 7.1.1 (Google Pixel). Works fine but looks hacky :P – jobbert Feb 02 '17 at 15:41
  • 4
    On API 21 emulator it crashes with "Caused by: java.lang.UnsupportedOperationException: Can't convert to dimension: type=0x1". – CoolMind Dec 19 '17 at 09:01
  • 1
    Binary XML file line #41: Can't convert value at index 4 to dimension: t – Pablo Cegarra Apr 10 '18 at 11:42
  • _7.1.2_: `Binary XML file line #45: Binary XML file line #45: You must supply a layout_height attribute.` – Shujito Jul 27 '18 at 19:51
45

Although this is an old question, I found that the answer didn't work in onCreate():

I found this code from here which does work in the onCreate() method

public int getStatusBarHeight() {
   int result = 0;
   int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
   if (resourceId > 0) {
      result = getResources().getDimensionPixelSize(resourceId);
   }
   return result;
}

I hope this helps to anyone that runs into this issue.

raukodraug
  • 11,519
  • 4
  • 35
  • 37
  • I used this code when I was creating live wallpaper. I couldn't find any activities to get `window`, so this function was useful for me! – Nolesh Nov 26 '13 at 09:37
11

The supported way of getting status bar height is to use WindowInsets class starting from API 21+:

customView.setOnApplyWindowInsetsListener((view, insets) -> {
    // Handle insets
    return insets.consumeSystemWindowInsets();
});

or WindowInsetsCompat for support libraries:

ViewCompat.setOnApplyWindowInsetsListener(customView, (view, insets) -> {
    // Handle insets
    return insets.consumeSystemWindowInsets();
});

You can also override the onApplyWindowInsets method inside the view:

public class CustomView extends View {
    @Override
    public WindowInsets onApplyWindowInsets(final WindowInsets insets) {
        final int statusBarHeight = insets.getStableInsetTop();
        return insets.consumeStableInsets();
    }
}

For further details, I'd recommend checking Chris Banes talk - Becoming a master window fitter (slides available here).

yarq
  • 111
  • 1
  • 3
  • @CoolMind Could you please share on which API's it doesn't work to double check this solution? – yarq Dec 20 '17 at 12:53
  • I tested on emulator with API 19, 21, 23. Probably I am mistaken, sorry. – CoolMind Dec 20 '17 at 12:58
  • 2
    the insets.consumeStableInsets() call has side-effects. Better to just call view.onApplyWindowInsets, which delegates back to the original window to deal with insets. – Robin Davies Feb 09 '20 at 14:38
  • I think this would be the preferred approach to handle system window insets when implementing edge-to-edge UI on Android (at the time of writing, at least). There is a great series of articles on Medium that goes into depth on the topic here: https://medium.com/androiddevelopers/gesture-navigation-going-edge-to-edge-812f62e4e83e. I can very much recommend reading them. – dbm Jun 04 '21 at 05:52
9

You could also take the dimension of the status bar found in the dimens.xml file of android using the way that this blog post describes.

This can get the height of the statusbar without the need to wait for drawing.

Quoting the code from the blog post:

public int getStatusBarHeight() {
  int result = 0;
  int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
  if (resourceId > 0) {
      result = getResources().getDimensionPixelSize(resourceId);
  }
  return result;
}

You need to put this method in a ContextWrapper class.

LKallipo
  • 790
  • 10
  • 18
6

I would suggest next code:

Rect rect = new Rect();
Window window = activity.getWindow();
if (window != null) {
  window.getDecorView().getWindowVisibleDisplayFrame(rect);
  android.view.View v = window.findViewById(Window.ID_ANDROID_CONTENT);

  android.view.Display display = ((android.view.WindowManager) activity.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();

  //return result title bar height
  return display.getHeight() - v.getBottom() + rect.top;   
}

Two examples:

1) Device 1 brand: samsung, device: maguro, board: tuna, cpu_abi: armeabi-v7a, display: ICL53F.M420KREL08, manufacturer: samsung, model: Galaxy Nexus, ver.release: 4.0.2, ver.sdk: 14;

Screen resolution: 1280 x 720.There are no hardware buttons on this device.

Results:

rect: left=0 right=720 top=72 bottom=1208;
v: left=0 right=720 top=72 bottom=1208;
display: height=1208 width=720;
correct result=72;

Device 1 has title bar at the top of the screen and status bar with software buttons at the bottom of the screen.

2) Device 2 device: bravo, board: bravo, cpu_abi: armeabi-v7a, display: FRG83G, manufacturer: HTC, model: HTC Desire, ver.release: 2.2.2, ver.sdk: 8,

Screen resolution: 800 x 400. This device has hardware buttons.

Results:

rect: left=0 right=480 top=38 bottom=800;
v: left=0 right=480 top=0 bottom=800;
display: height=800 width=480;
correct result: phone_bar_height=38;

Device 2 has title bar at the top of the screen and hasn't status bar at all.

Two solutions were suggested above:

A) v.getTop() - rect.top 

(it is incorrect for device 1 - it gives 0 instead of 72)

B) display.getHeight() - v.getHeight() 

(it is incorrect for device 2 - it gives 0 instead of 38)

Variant:

 display.getHeight() - v.getBottom() + rect.top

gives correct results in both cases.

Update

3) One more example (third device): brand: LGE, device: v909, cpu_abi: armeabi-v7a, display: HMJ37, model: LG-V909, ver.release: 3.1, ver.sdk: 12

rect: left=0 right=1280 top=0 bottom=720
v: left=0 right=1280 top=0 bottom=720
Display: height=768 width=1280
phone_bar_height=48

Device 3 has horizontal orientation, hasn't title bar at the top of the screen and has status bar at the bottom of the screen.

So, here:

int size_of_title_bar = rect.top;
int size_of_status_bar = display.getHeight() - v.getBottom();

It's correct for devices 2 and 3. I am not sure about device 1. User sent me screenshot of device 1. There is a status bar with software button there. But expression "display.getHeight() - v.getBottom()" gives 0.

dvpublic
  • 657
  • 8
  • 8
4

Attach a runnable to one of your views in your onCreate method, and place the above code in there. This will cause the application to calculate the status bar + titlescreen height when they are attached to the screen.

Take a look at the code below:

myView.post(new Runnable() {

        @Override
        public void run() {
            Rect rectgle= new Rect();
            Window window= getWindow();
            window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
            int StatusBarHeight= rectgle.top;
            int contentViewTop= window.findViewById(Window.ID_ANDROID_CONTENT).getTop();
            int TitleBarHeight= contentViewTop - StatusBarHeight;

        }

    });

If this still doesn't do the trick, try invoking the view's postDelayed method instead of post and adding a millisecond value as the second argument.

Kachi
  • 3,609
  • 2
  • 23
  • 18
  • Will you explain why this is safe? – tomwhipple Aug 11 '11 at 16:50
  • (return key cut me off...) The docs I see (http://developer.android.com/reference/java/lang/Runnable.html) suggest that this will start it's own thread. It is my understanding that interacting with the UI is not thread safe in general, and even if it were, the performance overhead for something simple like getting the status bar height is very high. – tomwhipple Aug 11 '11 at 17:00
  • this won't start a new thread, rather it adds the Runnable to the message queue on the UI thread. Take a look at the documentation for the post method in the View class. It is explained in there. http://developer.android.com/reference/android/view/View.html#post(java.lang.Runnable) – Kachi Feb 02 '12 at 15:30
  • @Felix I know that using View.post() works as you said (I use it myself to get the height of a view at runtime in onCreate()). But I never fully understood the mechanics behind it. The `view` that you call post() on is arbitrary correct? No matter which `view` you use the `Runnable` gets placed into the same UI-thread message queue? I know that it works but is it ever explained anywhere that the `post`ed runnable/message only gets run after the `view`s have been measured and layout() completed? – Tony Chan Feb 02 '12 at 22:15
3

This may seem unrelated but most of the time, the reason that people look for the status bar height is to offset their own views so they are not placed under it.

By setting fitsSystemWindows on the view you want to "push down" to give space to the status bar, it will be done automatically and according to the size of the status bar on each device. Padding will be added to the view that has this property set to true.

Keep in mind that padding will only be added to the first view in the hierarchy with fitSystemWindows set to true

This applies to cases where the status bar is translucent for example. Make sure that you set a Theme to the activity that doesn't have fitSystemWindows set, otherwise the padding will be added to the activity instead (because it's first in the hierarchy).

This article is a good read on the subject

lbarbosa
  • 2,042
  • 20
  • 23
  • Keep in mind that the consistent reason why people want the status bar height is because "padding will only be added to the FIRST view in the hierarchy, which causes endless problems in complex layouts. In addition CoordinatorLayouts and behaviours play havoc with fitsSystemWindows. – Robin Davies Feb 09 '20 at 15:07
2

As of API 23 there is a better solution to getting the status bar height. API 23 adds a WindowInsets feature, so you can use this code to get the size of the system insets, in this case at the top.

public int getStatusBarHeight() {
    if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
        return binding.mainContent.getRootWindowInsets().getStableInsetTop();
    }
    int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
    if(resourceId != 0) {
        return getResources().getDimensionPixelSize(resourceId);
    }
    return 0;
}

Note that getRootWindowInsets() will return null until after the View has been attached to a Window so it can't be used directly in onCreate() but you can add a listener for the window attach and do it there - here I am adding the status bar inset to the size of my toolbar, which I hide and show, along with the status bar. When it's shown, I want the status bar over the top of it so I add the status bar height to the toolbar's top padding.

    binding.mainContent.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
        @Override
        public void onViewAttachedToWindow(View view) {
            binding.toolbar.setPadding(binding.toolbar.getPaddingLeft(),
                binding.toolbar.getPaddingTop() + getStatusBarHeight(),
                binding.toolbar.getPaddingRight(), binding.toolbar.getPaddingBottom());
            binding.mainContent.removeOnAttachStateChangeListener(this);
        }

        @Override
        public void onViewDetachedFromWindow(View view) {

        }
    });
Clyde
  • 7,389
  • 5
  • 31
  • 57
2

Current actual way:

Kotlin:

ViewCompat.setOnApplyWindowInsetsListener(toolbar) { view, windowInsets ->
     val insets = windowInsets.getInsets(WindowInsetsCompat.Type.statusBars())
     view.updateLayoutParams<MarginLayoutParams> {
         topMargin = insets.top
     }
     WindowInsetsCompat.CONSUMED
}

Java:

ViewCompat.setOnApplyWindowInsetsListener(toolbar, (v, windowInsets) -> {
    Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
    ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
    mlp.topMargin = insets.top;
    v.setLayoutParams(mlp);
    return WindowInsetsCompat.CONSUMED;
}

Google recommends using it like this if you want to support edge-to-edge in your app.

Rado
  • 133
  • 2
  • 7
sergpetrov
  • 1,506
  • 1
  • 11
  • 20
1

The solution posted by Jorgesys is very good, but it doesn't work inside onCreate() method. I guess it's because statusbar and titlebar are created after onCreate().

The solution is easy - you should put code inside runnable and execute it after onCreate() by using root.post((Runnable action) );

So the whole solution:

root = (ViewGroup)findViewById(R.id.root);
root.post(new Runnable() { 
        public void run(){
            Rect rectgle= new Rect();
            Window window= getWindow();
            window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
            int StatusBarHeight= rectgle.top;
            int contentViewTop= 
                 window.findViewById(Window.ID_ANDROID_CONTENT).getTop();
            int TitleBarHeight= contentViewTop - StatusBarHeight;

            Log.i("***  jakkubu :: ", "StatusBar Height= " + StatusBarHeight +
               " , TitleBar Height = " + TitleBarHeight);
        }
});

I find it here

jakkubu
  • 43
  • 1
  • 4
1

Ok. Final answer!!! One that does not have side-effects, relies on documented behavior, supports Q, cutouts, devices with different status-bar size depending on orientation, &c.

protected void onCreate(Bundle savedInstanceState) {
     ...
     setContentView(R.layout.activity_main);
     ....
     // Use The topmost view of the activity, which 
    // is guaranteed to be asked about window insets/
     View rootView = findViewById(R.id.root_view_of_your_activity);
     ViewCompat.setOnApplyWindowInsetsListener(rootView, new OnApplyWindowInsetsListener() {
        @Override
        public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) 
    {
        //THIS is the value you want.
        statusBarHeight = insets.getSystemWindowInsetTop();

        // Let the view handle insets as it likes.
        return ViewCompat.onApplyWindowInsets(v,insets);
    }
});

The callback occurs after onStart(), before first layout, and occasionally thereafter.

Robin Davies
  • 7,547
  • 1
  • 35
  • 50
1

I think better way to calculate that is to get height of fullscreen minus our main layout

phoneBarsHeight = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getHeight() 
    - mainView.getHeight();

And you can put it in onGlobalLayout(). This works on tablets too, I tried it on Theme.NoTitleBar, but it must always works.

Maybe you can even enhance it and use it onCreate() by changing mainView.getHeight() to mainView.getMeasuredHeight().

hasanghaforian
  • 13,858
  • 11
  • 76
  • 167
Mikooos
  • 5,360
  • 5
  • 31
  • 45
  • getMeasuredHeight() returns 0 in the onCreate unfortunately. – Ben Clayton Jan 08 '13 at 09:52
  • It might depends on platform, use this to get first information about first measurement http://developer.android.com/reference/android/view/ViewTreeObserver.html – Mikooos Jan 08 '13 at 14:48
  • 1
    OnCreate is too early. You can call getMeasuredHeight on a view in onResume. That is the callback by when your views should be visible. – speedynomads Jun 10 '13 at 09:57
0

Targeting API 30, I've used successfully updated Nicklas answer (ref:https://stackoverflow.com/a/47125610/2163045)

In my example I'm adjusting dynamically custom toolbar height in fullscreen WindowCompat.setDecorFitsSystemWindows(window, false) Activity

Tested on GooglePixel 5

class MyActivity : ViewBindingActivity<LayoutBinding>() {
...

    override fun created(binding: LayoutBinding, savedInstanceState: Bundle?) {

        binding.root.setOnApplyWindowInsetsListener { _, insets ->
            val statusBarHeight = insets.getInsets(WindowInsets.Type.statusBars()).top // <- HERE YOU ARE
            val toolbarHeight = getDimenPx(R.dimen.toolbar_height)
            binding.toolbar.layoutParams.height = statusBarHeight + toolbarHeight
            insets
        }
    }
}
murt
  • 3,790
  • 4
  • 37
  • 48