Android Design Development

Modifying Android's Navigation Bar for a More Immersive Experience

March 20, 2018

A majority of Android devices have a software navigation bar which (for every other app I can think of) is a solid black (or white) bar that takes up 48dp of your vertical screen space. On the Google Pixel this equates to somewhere between 7-10% of the total vertical screen space (depending on your accessibility settings for screen size). Considering that most mobile apps are optimized for showing you a list of things in a window that scrolls vertically, we might want to reclaim some of this space for the content of our apps. This will make your app feel more immersive by using more of the screen to display your content. You could certainly hide the navigation UI altogether, but this makes it more difficult for the user to navigate between screens in your app because they will have to swipe up from the bottom of the screen to reveal the hidden navigation bar. I stumbled on a better answer by accident when I noticed that the news feed section of the Pixel launcher has a semi-transparent bottom navigation bar:

Google Feed

As it turns out, this translucent navbar option has been around since KitKat (API Level 19)! All you have to do is set android:windowTranslucentNavigation to true and it just works, right?

The Part Where it Gets Complicated

Nope, sorry, I lied. Even though we've requested that the app be shown with a translucent navigation bar, the app's window still is sized to the area between the navigation bar and the statusbar. Fortunately, the KitKat release notes helpfully explain that in order to get your app to display content under the navigation bar, you must also set android:fitsSystemWindows to false. This allows the Activity's window to be drawn at the full size of the device's screen.

By this point, you've probably wondered "How do I tap on items that are displayed underneath the navigation bar?" The answer is "You can't" because the navigation bar does not pass touch events through to your application. Therefore, we have to add padding to the bottom of the app's content so that it can be scrolled into an accessible position above the navigation bar. The amount of padding required is simply the height of the navbar which be determined by retrieving the value of android.R.dimen.navigation_bar_height from the system resources:

`kotlin val Resources.navBarHeight: Int @Px get() { val id = getIdentifier("navigation_bar_height", "dimen", "android") return when {

id > 0 -> getDimensionPixelSize(id)
else -> 0

} } `

Then apply that many pixels of padding to the bottom of your layout's scrolling element (in this example, a RecyclerView):

`kotlin recyclerView.setPaddingRelative( recyclerView.paddingStart, recyclerView.paddingTop, recyclerView.paddingEnd, recyclerView.paddingBottom + resources.navBarHeight ) `

You will also need to set android:clipToPadding to false on your RecyclerView or ScrollView otherwise Android will not draw any child views inside the padding area.

What About Hardware Navigation Buttons?

Phones such as the Samsung Galaxy S7 use physical buttons for the Back/Home/Switcher actions and do not show the software navigation bar on the screen. Some of these even let you choose between the hardware buttons and the onscreen buttons. Fortunately, we can read android.R.bool.config_showNavigationBar to determine if the software navbar is being shown:

`kotlin val Resources.showsSoftwareNavBar: Boolean get() { val id = getIdentifier("config_showNavigationBar", "bool", "android") return id > 0 && getBoolean(id) }

// Inset bottom of content if drawing under the translucent navbar, but // only if the navbar is a software bar if (resources.showsSoftwareNavBar) { recyclerView.setPaddingRelative(/ ... /) } `

Everything Goes Sideways

Rotation configuration changes have always been tricky for Android apps to handle and this is no different. When you rotate a phone into the 90° or 270° orientation, the navigation bar remains on the side of the phone that is the "bottom" in the 0° orientation. In these orientations, displaying content under the navbar loses its usefulness, so we'll disable it. The easiest way to do this is by overriding these attributes of the activity's theme based on orientation:

`xml false true

true false


We will also have to add the padding to the scrolling content of the view conditionally:

`kotlin inline val Resources.isNavBarAtBottom: Boolean get() { // Navbar is always on the bottom of the screen in portrait mode, but rotates // with device in landscape orientations return this.configuration.orientation == ORIENTATION_PORTRAIT }

// Inset bottom of content if drawing under the translucent navbar, but // only if the navbar is a software bar and is on the bottom of the screen. if (resources.showsSoftwareNavBar && resources.isNavBarAtBottom) { recyclerView.setPaddingRelative(/ ... /) } `

But wait! What about tablets? Tablets do rotate the navbar to the bottom of the screen in all orientations. There is no isTablet property provided by Android since the definition of "tablet" is rather ambiguous in the presence of large "phablet" phones, so we have to create our own. Android treats devices with a screen whose smallest dimension is at least 600dp differently, most importantly for our use case, letting the navigation bar rotate. We can again use resource splits to create different values for isTablet depending on the device's screen that we can check in our code:

`xml false



`kotlin inline val Resources.isTablet: Boolean get() = getBoolean(R.bool.is_tablet)

inline val Resources.isNavBarAtBottom: Boolean get() { // Navbar is always on the bottom of the screen in portrait mode, but may // rotate with device if its category is sw600dp or above. return this.isTablet || this.configuration.orientation == ORIENTATION_PORTRAIT } `

Catch 24

Android 7.0 Nougat (API 24) introduced yet another element for us to worry about: Mutli-Window mode. In Multi-Window mode, two activities may be shown side-by-side or stacked vertically depending on device orientation and neither app gets to draw under the navigation bar. Since we cannot perform any of our fancy navigation bar styling in these cases, we will disable our modifications to the activity theme and padding applied to the content in Multi-Window mode:

`kotlin inline val Activity.isInMultiWindow: Boolean get() { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {


} else {


} }

// Apps can't draw under the navbar in multiwindow mode. val fitSystemWindows = if (activity?.isInMultiWindow == true) { true } else { resources.getBoolean(R.bool.fullscreen_style_fit_system_windows) } // Override the activity's theme when in multiwindow mode. coordinatorLayout.fitsSystemWindows = fitSystemWindows

if (!fitSystemWindows) { // Inset bottom of content if drawing under the translucent navbar, but // only if the navbar is a software bar and is on the bottom of the screen. if (resources.showsSoftwareNavBar && resources.isNavBarAtBottom) {

recyclerView.setPaddingRelative(/* ... */)

} } `

One More Thing

With fitsSystemWindows="false", the Activity is also drawn underneath the statusbar, which we have completely ignored so far. Fortunately, its behavior is much more predictable than the navigation bar since it is always shown on all phones (except when explicitly hidden by the activity theme, which we did not do), and it is always shown at the top of the screen. Thus, whether we add padding to our AppBarLayout or not will follow the fitSystemWindows setting with no exceptions:

`kotlin val Resources.statusBarHeight: Int @Px get() { val id = getIdentifier("status_bar_height", "dimen", "android") return when {

id > 0 -> getDimensionPixelSize(id)
else -> 0

} }

if (!fitSystemWindows) { // ... // Inset the toolbar when it is drawn under the status bar. barLayout.updatePaddingRelative(

barLayout.paddingTop + resources.statusBarHeight,

) } `

A Piece of Everyone's Least Favorite Candy

Right at the beginning, I said that KitKat was the Android version that enabled use of translucent navigation bars, but like so many things in KitKat, you can't get it to work properly. The config_showNavigationBar value that we check to see if we need to inset the scrolling content always returns false when fitsSystemWindows is false. The navigation_bar_height dimension also always returns 0 in this case, making it impossible to measure how much padding we should add to the content. To implement our solution on KitKat we have to assume the device always has a software navigation bar (which is less and less likely the older the device is), estimate the padding required (almost always 48dp, 56dp for tablets meeting the sw900dp qualifier), and just deal with excess space at the bottom for devices that don't. That's too many conditions to leave to chance (especially for KitKat), so I've opted to disable the translucent navbar using another resource split:

`xml true true




There's a lot of stuff to keep in mind here, so I've made a sample app demonstrating all these concepts available on GitHub. If you'd like to play around with the app, it is also available on the Google Play Store. It's a very simple example that shows the colors of the Material palette in a grid:

Translucent Navbar

Josh Friend
Josh Friend
Development Practice Co-Lead

Stay in the loop with our latest content!

Select the topics you’re interested to receive our new relevant content in your inbox. Don’t worry, we won’t spam you.

Using View Model Protocols to manage complex SwiftUI Views

Using View Model Protocols to manage complex SwiftUI Views

March 11, 2021

Managing complex screens or views that depend on asynchronous services or the need to pull in state from across your app can be tricky to get right. The most common way to address this in SwiftUI is by abstracting that logic into a dedicated view model for that piece of UI.

Read more
Creating an Emotionally Safe Workplace: We All Play a Role

Creating an Emotionally Safe Workplace: We All Play a Role

March 4, 2021

Read more
2022 Best and Brightest Winners in West Michigan

2022 Best and Brightest Winners in West Michigan

May 2, 2022

The Best and Brightest Companies to Work For® competition identifies and honors organizations that display a commitment to excellence in operations and employee enrichment that lead to increased productivity and financial performance!

Read more
View more articles