How do I get the height and width of the Android Navigation Bar programmatically?

Learn how do i get the height and width of the android navigation bar programmatically? with practical examples, diagrams, and best practices. Covers android, user-interface, graphics development t...

Programmatically Obtaining Android Navigation Bar Dimensions

Hero image for How do I get the height and width of the Android Navigation Bar programmatically?

Learn how to accurately retrieve the height and width of the Android system navigation bar using various programmatic approaches, ensuring your app's UI adapts correctly.

Developing Android applications often requires precise control over UI layout to ensure a seamless user experience across a multitude of devices. A common challenge arises when dealing with system UI elements like the navigation bar. Depending on the device, Android version, and user settings, the navigation bar can be present at the bottom or side of the screen, or even hidden. Knowing its dimensions programmatically is crucial for tasks such as adjusting view paddings, calculating available screen space, or preventing UI elements from being obscured.

Understanding the Android Navigation Bar

The Android navigation bar typically houses the 'Back', 'Home', and 'Recents' buttons. Its position and visibility can vary:

  • Bottom: Most common configuration for phones.
  • Side (Right/Left): Common on tablets or foldable devices in certain orientations.
  • Hidden: When an app enters immersive mode or the user has gesture navigation enabled.

Accurately determining its size is not always straightforward, as it's a system-level UI component. We'll explore methods that account for these variations.

flowchart TD
    A[Start] --> B{Is Navigation Bar Visible?}
    B -- Yes --> C{Get Display Metrics}
    C --> D{Calculate Real Screen Size}
    D --> E{Calculate App Window Size}
    E --> F{Determine Nav Bar Height/Width}
    B -- No --> G[Nav Bar Dimensions = 0]
    F --> H[End]
    G --> H

Flowchart for determining navigation bar dimensions

For modern Android development (API level 21 and above), WindowInsets provide a robust and accurate way to determine the dimensions of system UI elements, including the navigation bar. This approach is preferred because it correctly accounts for various system UI states and configurations, such as gesture navigation or immersive mode.

WindowInsets are delivered to views when the system UI changes. You can listen for these changes and query the insets for the navigation bar.

import android.graphics.Rect
import android.os.Build
import android.view.View
import android.view.WindowInsets
import androidx.annotation.RequiresApi

@RequiresApi(Build.VERSION_CODES.R)
fun getNavigationBarHeightApi30(view: View, callback: (Int) -> Unit) {
    view.setOnApplyWindowInsetsListener { v, insets ->
        val systemInsets = insets.getInsets(WindowInsets.Type.systemBars())
        val navBarInsets = insets.getInsets(WindowInsets.Type.navigationBars())

        // The navigation bar height is typically the bottom inset of navigationBars()
        // or the difference between systemBars() and statusBars() if nav bar is at bottom
        val navBarHeight = navBarInsets.bottom
        callback(navBarHeight)
        insets
    }
    // Request insets dispatch if not already dispatched
    view.requestApplyInsets()
}

@RequiresApi(Build.VERSION_CODES.Q)
fun getNavigationBarHeightApi29(view: View, callback: (Int) -> Unit) {
    view.setOnApplyWindowInsetsListener { v, insets ->
        val navBarInsets = insets.getInsets(WindowInsets.Type.navigationBars())
        callback(navBarInsets.bottom)
        insets
    }
    view.requestApplyInsets()
}

// For older APIs (21-28), you might need to combine with other methods or use deprecated ones.
// A more general approach for API 21+ can be found using WindowInsetsCompat from AndroidX.

// Example usage in an Activity or Fragment:
// override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
//     super.onViewCreated(view, savedInstanceState)
//     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
//         getNavigationBarHeightApi30(view) { height ->
//             Log.d("NavBar", "Navigation Bar Height (API 30+): $height px")
//         }
//     } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
//         getNavigationBarHeightApi29(view) { height ->
//             Log.d("NavBar", "Navigation Bar Height (API 29): $height px")
//         }
//     } else {
//         // Fallback for older APIs or use WindowInsetsCompat
//         val navBarHeight = getNavigationBarHeightLegacy(requireActivity())
//         Log.d("NavBar", "Navigation Bar Height (Legacy): $navBarHeight px")
//     }
// }

Method 2: Calculating Difference Between Real and App Display Size (Legacy)

For older Android versions (pre-API 29) or as a fallback, you can calculate the navigation bar's dimensions by comparing the total screen size (including system decorations) with the application's usable display size. This method is less reliable than WindowInsets but can work in many scenarios.

This approach involves:

  1. Getting the 'real' screen dimensions using DisplayMetrics and getRealSize().
  2. Getting the application's usable screen dimensions using DisplayMetrics and getSize() or getRectSize().
  3. The difference in height (or width, for side navigation bars) often corresponds to the navigation bar's size.
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Build;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.WindowManager;

public class NavigationBarUtils {

    public static int getNavigationBarHeight(Context context) {
        WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        if (windowManager == null) {
            return 0;
        }

        Display display = windowManager.getDefaultDisplay();
        DisplayMetrics realMetrics = new DisplayMetrics();
        display.getRealMetrics(realMetrics);

        int realHeight = realMetrics.heightPixels;
        int realWidth = realMetrics.widthPixels;

        DisplayMetrics displayMetrics = new DisplayMetrics();
        display.getMetrics(displayMetrics);

        int displayHeight = displayMetrics.heightPixels;
        int displayWidth = displayMetrics.widthPixels;

        // Check if navigation bar is at the bottom
        if (realHeight > displayHeight) {
            return realHeight - displayHeight;
        }
        // Check if navigation bar is on the side (e.g., landscape tablets)
        else if (realWidth > displayWidth) {
            return realWidth - displayWidth; // This would be the width of the side nav bar
        }
        return 0;
    }

    // Another legacy approach using resources, less reliable for dynamic changes
    public static int getNavigationBarHeightUsingResources(Context context) {
        int resourceId = context.getResources().getIdentifier("navigation_bar_height", "dimen", "android");
        if (resourceId > 0) {
            return context.getResources().getDimensionPixelSize(resourceId);
        }
        return 0;
    }

    // Example usage in an Activity:
    // @Override
    // protected void onCreate(Bundle savedInstanceState) {
    //     super.onCreate(savedInstanceState);
    //     setContentView(R.layout.activity_main);
    //
    //     int navBarHeight = NavigationBarUtils.getNavigationBarHeight(this);
    //     Log.d("NavBar", "Navigation Bar Height (Legacy Calc): " + navBarHeight + " px");
    //
    //     int navBarHeightRes = NavigationBarUtils.getNavigationBarHeightUsingResources(this);
    //     Log.d("NavBar", "Navigation Bar Height (Legacy Res): " + navBarHeightRes + " px");
    // }
}

Handling Gesture Navigation

With the introduction of gesture navigation, the traditional navigation bar may no longer be present as a distinct, fixed-size UI element. Instead, the system might draw a small 'gesture bar' at the bottom. WindowInsets are still the best way to handle this, as WindowInsets.Type.navigationBars() will return the space reserved for these gestures, allowing your app to avoid overlapping them.

When gesture navigation is active, the navigation_bar_height resource might return 0 or a very small value, and the difference calculation method might also yield 0. This is the expected behavior, as the system is giving your app more screen real estate.

1. Choose the Right API Level

For API 29 (Android 10) and above, use WindowInsets.Type.navigationBars() with setOnApplyWindowInsetsListener. This is the most accurate and future-proof method.

2. Implement WindowInsets Listener

Attach an OnApplyWindowInsetsListener to your root view. This listener will be invoked whenever the system UI insets change, providing up-to-date information.

3. Extract Navigation Bar Insets

Inside the listener, use insets.getInsets(WindowInsets.Type.navigationBars()) to get the Insets object specifically for the navigation bar. Its bottom, left, or right fields will give you the dimensions.

4. Consider Fallbacks (if necessary)

For older API levels (pre-29), you might need to use the getNavigationBarHeight calculation method or getNavigationBarHeightUsingResources as a fallback, though these are less reliable.

5. Apply Dimensions to UI

Once you have the dimensions, apply them as padding or margins to your views to prevent content from being obscured by the navigation bar.