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

AndroidUser InterfaceGraphicsAndroid Activity

Android Problem Overview


The black navigation bar on the bottom of the screen is not easily removable in Android. It has been part of Android since 3.0 as a replacement for hardware buttons. Here is a picture:

System Bar

How can I get the size of the width and the height of this UI element in pixels?

Android Solutions


Solution 1 - Android

Try below code:

Resources resources = context.getResources();
int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
    return resources.getDimensionPixelSize(resourceId);
}
return 0;

Solution 2 - Android

I get navigation bar size by comparing app-usable screen size with real screen size. I assume that navigation bar is present when app-usable screen size is smaller than real screen size. Then I calculate navigation bar size. This method works with API 14 and up.

public static Point getNavigationBarSize(Context context) {
    Point appUsableSize = getAppUsableScreenSize(context);
    Point realScreenSize = getRealScreenSize(context);

    // navigation bar on the side
    if (appUsableSize.x < realScreenSize.x) {
        return new Point(realScreenSize.x - appUsableSize.x, appUsableSize.y);
    }

    // navigation bar at the bottom
    if (appUsableSize.y < realScreenSize.y) {
        return new Point(appUsableSize.x, realScreenSize.y - appUsableSize.y);
    }

    // navigation bar is not present
    return new Point();
}

public static Point getAppUsableScreenSize(Context context) {
    WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    Display display = windowManager.getDefaultDisplay();
    Point size = new Point();
    display.getSize(size);
    return size;
}

public static Point getRealScreenSize(Context context) {
    WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    Display display = windowManager.getDefaultDisplay();
    Point size = new Point();

    if (Build.VERSION.SDK_INT >= 17) {
        display.getRealSize(size);
    } else if (Build.VERSION.SDK_INT >= 14) {
        try {
            size.x = (Integer) Display.class.getMethod("getRawWidth").invoke(display);
            size.y = (Integer) Display.class.getMethod("getRawHeight").invoke(display);
        } catch (IllegalAccessException e) {} catch (InvocationTargetException e) {} catch (NoSuchMethodException e) {}
    }

    return size;
}

UPDATE

For a solution that takes into account display cutouts please check John's answer.

Solution 3 - Android

The NavigationBar height varies for some devices, but as well for some orientations. First you have to check if the device has a navbar, then if the device is a tablet or a not-tablet (phone) and finally you have to look at the orientation of the device in order to get the correct height.

public int getNavBarHeight(Context c) {
    	 int result = 0;
    	 boolean hasMenuKey = ViewConfiguration.get(c).hasPermanentMenuKey();
    	 boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);

    	 if(!hasMenuKey && !hasBackKey) {
    	     //The device has a navigation bar
    		 Resources resources = c.getResources();

    		 int orientation = resources.getConfiguration().orientation;
    		 int resourceId;
    		 if (isTablet(c)){
    			 resourceId = resources.getIdentifier(orientation == Configuration.ORIENTATION_PORTRAIT ? "navigation_bar_height" : "navigation_bar_height_landscape", "dimen", "android");
    		 }  else {
    			 resourceId = resources.getIdentifier(orientation == Configuration.ORIENTATION_PORTRAIT ? "navigation_bar_height" : "navigation_bar_width", "dimen", "android");	 
    		 }
         
    		 if (resourceId > 0) {
    			 return resources.getDimensionPixelSize(resourceId);
    		 }
    	 }
         return result;
} 


private boolean isTablet(Context c) {
	return (c.getResources().getConfiguration().screenLayout
            & Configuration.SCREENLAYOUT_SIZE_MASK)
            >= Configuration.SCREENLAYOUT_SIZE_LARGE;
}

Solution 4 - Android

Actually the navigation bar on tablets (at least Nexus 7) has different size in portrait and landscape so this function should look like this:

private int getNavigationBarHeight(Context context, int orientation) {
    Resources resources = context.getResources();

    int id = resources.getIdentifier(
            orientation == Configuration.ORIENTATION_PORTRAIT ? "navigation_bar_height" : "navigation_bar_height_landscape",
            "dimen", "android");
    if (id > 0) {
        return resources.getDimensionPixelSize(id);
    }
    return 0;
}

Solution 5 - Android

I think better answer is here because it allows you to get even cutout height too.

Take your root view, and add setOnApplyWindowInsetsListener (or you can override onApplyWindowInsets from it), and take insets from it.

In my camera activity, i add padding equal to the systemBars.bottom to my bottom layout. And finally, it fix cutout issue.

Camera activity insets

with appcompat it is like this

ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, insets ->
    val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
    binding.takePictureLayout.apply {
        setPaddingRelative(paddingStart, paddingTop, paddingEnd, systemBars.bottom)
    }
    return@setOnApplyWindowInsetsListener insets
}

without appcompat, this:

mCameraSourcePreview.setOnApplyWindowInsetsListener((v, insets) -> { ... })

Solution 6 - Android

I hope this helps you

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

public int getNavigationBarHeight()
{
    boolean hasMenuKey = ViewConfiguration.get(context).hasPermanentMenuKey();
    int resourceId = getResources().getIdentifier("navigation_bar_height", "dimen", "android");
    if (resourceId > 0 && !hasMenuKey)
    {
        return getResources().getDimensionPixelSize(resourceId);
    }
    return 0;
}

Solution 7 - Android

This is my code to add paddingRight and paddingBottom to a View to dodge the Navigation Bar. I combined some of the answers here and made a special clause for landscape orientation together with isInMultiWindowMode. The key is to read navigation_bar_height, but also check config_showNavigationBar to make sure we should actually use the height.

None of the previous solutions worked for me. As of Android 7.0 you have to take Multi Window Mode into consideration. This breaks the implementations comparing display.realSize with display.size since realSize gives you the dimensions of the whole screen (both split windows) and size only gives you the dimensions of your App window. Setting padding to this difference will leave your whole view being padding.

/** Adds padding to a view to dodge the navigation bar.

    Unfortunately something like this needs to be done since there
    are no attr or dimens value available to get the navigation bar
    height (as of December 2016). */
public static void addNavigationBarPadding(Activity context, View v) {
    Resources resources = context.getResources();
    if (hasNavigationBar(resources)) {
        int orientation = resources.getConfiguration().orientation;
        int size = getNavigationBarSize(resources);
        switch (orientation) {
        case Configuration.ORIENTATION_LANDSCAPE:
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N &&
                context.isInMultiWindowMode()) { break; }
            v.setPadding(v.getPaddingLeft(), v.getPaddingTop(),
                         v.getPaddingRight() + size, v.getPaddingBottom());
            break;
        case Configuration.ORIENTATION_PORTRAIT:
            v.setPadding(v.getPaddingLeft(), v.getPaddingTop(),
                         v.getPaddingRight(), v.getPaddingBottom() + size);
            break;
        }
    }
}

private static int getNavigationBarSize(Resources resources) {
    int resourceId = resources.getIdentifier("navigation_bar_height",
                                             "dimen", "android");
    return resourceId > 0 ? resources.getDimensionPixelSize(resourceId) : 0;
}

private static boolean hasNavigationBar(Resources resources) {
    int hasNavBarId = resources.getIdentifier("config_showNavigationBar",
                                              "bool", "android");
    return hasNavBarId > 0 && resources.getBoolean(hasNavBarId);
}

Solution 8 - Android

New answer in 2021 comes to the rescue


insipred from Egis's answer:

context.navigationBarHeight

where the extension getter is

val Context.navigationBarHeight: Int
get() {
	val windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager

	return if (Build.VERSION.SDK_INT >= 30) {
		windowManager
				.currentWindowMetrics
				.windowInsets
				.getInsets(WindowInsets.Type.navigationBars())
				.bottom

	} else {
		val currentDisplay = try {
			display
		} catch (e: NoSuchMethodError) {
			windowManager.defaultDisplay
		}

		val appUsableSize = Point()
		val realScreenSize = Point()
		currentDisplay?.apply {
			getSize(appUsableSize)
			getRealSize(realScreenSize)
		}

		// navigation bar on the side
		if (appUsableSize.x < realScreenSize.x) {
			return realScreenSize.x - appUsableSize.x
		}

		// navigation bar at the bottom
		return if (appUsableSize.y < realScreenSize.y) {
			realScreenSize.y - appUsableSize.y
		} else 0
	}
}

tested on:

  • emulators with navigation bars

    • pixel 3a (api 30)
    • pixel 2 (api 28)
    • pixel 3 (api 25)
    • pixel 2 (api 21)
  • Xiaomi Poco f2 pro with & without navigation bar(full display)

Solution 9 - Android

The solution proposed by Egidijus and works perfectly for Build.VERSION.SDK_INT >= 17

But I got "NoSuchMethodException" during execution of the following statement with Build.VERSION.SDK_INT < 17 on my device:

Display.class.getMethod("getRawHeight").invoke(display);

I have modified the method getRealScreenSize() for such cases:

else if(Build.VERSION.SDK_INT >= 14) 
{
    View decorView = getActivity().getWindow().getDecorView();
    size.x = decorView.getWidth();
    size.y = decorView.getHeight();
}

Solution 10 - Android

I resolved this issue for all devices(including Nexus 5, Samsung Galaxy Nexus 6 edge+, Samsung S10, Samsung Note II etc.). I think this will help you to handle device dependant issues.

Here I am adding two types of codes,

Java Code(for Native Android):

import android.content.Context;
import android.content.res.Resources;
import android.os.Build;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.ViewConfiguration;
import android.view.WindowManager;

public class DeviceSpec {

    private int resourceID = -1;
    private Display display = null;
    private DisplayMetrics displayMetrics = null;
    private DisplayMetrics realDisplayMetrics = null;
    private Resources resources = null;
    private WindowManager windowManager = null;

    public double GetNavigationBarHeight(Context context) {
        try {
            windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
            display = windowManager.getDefaultDisplay();
            displayMetrics = new DisplayMetrics();
            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
                realDisplayMetrics = new DisplayMetrics();
                display.getMetrics(displayMetrics);
                display.getRealMetrics(realDisplayMetrics);
                if(displayMetrics.heightPixels != realDisplayMetrics.heightPixels) {
                    resources = context.getResources();
                    return GetNavigationBarSize(context);
                }
            }
            else {
                resources = context.getResources();
                resourceID = resources.getIdentifier("config_showNavigationBar", "bool", "android");
                if (resourceID > 0 && resources.getBoolean(resourceID))
                    return GetNavigationBarSize(context);
            }
        }
        catch (Exception e){
            e.printStackTrace();
        }
        return 0;
    }

    private double GetNavigationBarSize(Context context) {
        resourceID = resources.getIdentifier("navigation_bar_height", "dimen", "android");
        if (resourceID > 0 && ViewConfiguration.get(context).hasPermanentMenuKey())
           return (resources.getDimensionPixelSize(resourceID) / displayMetrics.density);
        return 0;
    }
}

And C# code(for Xamarin Forms/Android)

int resourceId = -1;
		IWindowManager windowManager = null;
		Display defaultDisplay = null;
		DisplayMetrics displayMatrics = null;
		DisplayMetrics realMatrics = null;
		Resources resources = null;

		public double NavigationBarHeight
		{
			get
			{
				try
				{
					windowManager = Forms.Context.GetSystemService(Context.WindowService).JavaCast<IWindowManager>();
					defaultDisplay = windowManager.DefaultDisplay;
					displayMatrics = new DisplayMetrics();
					if (Build.VERSION.SdkInt >= BuildVersionCodes.JellyBeanMr2)
					{
						realMatrics = new DisplayMetrics();
						defaultDisplay.GetMetrics(displayMatrics);
						defaultDisplay.GetRealMetrics(realMatrics);
						if (displayMatrics.HeightPixels != realMatrics.HeightPixels)
						{
							resources = Forms.Context.Resources;
							return GetHeightOfNivigationBar();
						}
					}
					else {
						resources = Forms.Context.Resources;
						resourceId = resources.GetIdentifier("config_showNavigationBar", "bool", "android");
						if (resourceId > 0 && resources.GetBoolean(resourceId))
							return GetHeightOfNivigationBar();
					}
				}
				catch (Exception e) { }
				return 0;
			}
		}

		private double GetHeightOfNivigationBar()
		{
			resourceId = resources.GetIdentifier("navigation_bar_height", "dimen", "android");
			if (!ViewConfiguration.Get(Forms.Context).HasPermanentMenuKey && resourceId > 0)
			{
				return resources.GetDimensionPixelSize(resourceId) / displayMatrics.Density;
			}
			return 0;
		}

Solution 11 - Android

Tested code for getting height of navigation bar (in pixels):

public static int getNavBarHeight(Context c) {
    int resourceId = c.getResources()
                      .getIdentifier("navigation_bar_height", "dimen", "android");
    if (resourceId > 0) {
        return c.getResources().getDimensionPixelSize(resourceId);
    }
    return 0;
}

Tested code for getting height of status bar (in pixels):

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

Converting pixels to dp:

public static int pxToDp(int px) {
    return (int) (px / Resources.getSystem().getDisplayMetrics().density);
}

Solution 12 - Android

How to get the height of the navigation bar and status bar. This code works for me on some Huawei devices and Samsung devices. Egis's solution above is good, however, it is still incorrect on some devices. So, I improved it.

This is code to get the height of status bar

private fun getStatusBarHeight(resources: Resources): Int {
        var result = 0
        val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
        if (resourceId > 0) {
            result = resources.getDimensionPixelSize(resourceId)
        }
        return result
    }

This method always returns the height of navigation bar even when the navigation bar is hidden.

private fun getNavigationBarHeight(resources: Resources): Int {
    val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android")
    return if (resourceId > 0) {
        resources.getDimensionPixelSize(resourceId)
    } else 0
}

NOTE: on Samsung A70, this method returns the height of the status bar + height of the navigation bar. On other devices (Huawei), it only returns the height of the Navigation bar and returns 0 when the navigation bar is hidden.

private fun getNavigationBarHeight(): Int {
        val display = activity?.windowManager?.defaultDisplay
        return if (display == null) {
            0
        } else {
            val realMetrics = DisplayMetrics()
            display.getRealMetrics(realMetrics)
            val metrics = DisplayMetrics()
            display.getMetrics(metrics)
            realMetrics.heightPixels - metrics.heightPixels
        }
    }

This is code to get height of navigation bar and status bar

val metrics = DisplayMetrics()
        activity?.windowManager?.defaultDisplay?.getRealMetrics(metrics)

        //resources is got from activity

        //NOTE: on SamSung A70, this height = height of status bar + height of Navigation bar
        //On other devices (Huawei), this height = height of Navigation bar
        val navigationBarHeightOrNavigationBarPlusStatusBarHeight = getNavigationBarHeight()

        val statusBarHeight = getStatusBarHeight(resources)
        //The method will always return the height of navigation bar even when the navigation bar was hidden.
        val realNavigationBarHeight = getNavigationBarHeight(resources)

        val realHeightOfStatusBarAndNavigationBar =
                if (navigationBarHeightOrNavigationBarPlusStatusBarHeight == 0 || navigationBarHeightOrNavigationBarPlusStatusBarHeight < statusBarHeight) {
                    //Huawei: navigation bar is hidden
                    statusBarHeight
                } else if (navigationBarHeightOrNavigationBarPlusStatusBarHeight == realNavigationBarHeight) {
                    //Huawei: navigation bar is visible
                    statusBarHeight + realNavigationBarHeight
                } else if (navigationBarHeightOrNavigationBarPlusStatusBarHeight < realNavigationBarHeight) {
                    //SamSung A70: navigation bar is still visible but it only displays as a under line
                    //navigationBarHeightOrNavigationBarPlusStatusBarHeight = navigationBarHeight'(under line) + statusBarHeight
                    navigationBarHeightOrNavigationBarPlusStatusBarHeight
                } else {
                    //SamSung A70: navigation bar is visible
                    //navigationBarHeightOrNavigationBarPlusStatusBarHeight == statusBarHeight + realNavigationBarHeight
                    navigationBarHeightOrNavigationBarPlusStatusBarHeight
                }

Solution 13 - Android

I've done this, it works on every device I tested, and even on emulators:

// Return the NavigationBar height in pixels if it is present, otherwise return 0
public static int getNavigationBarHeight(Activity activity) {
    Rect rectangle = new Rect();
    DisplayMetrics displayMetrics = new DisplayMetrics();
    activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(rectangle);
    activity.getWindowManager().getDefaultDisplay().getRealMetrics(displayMetrics);
    return displayMetrics.heightPixels - (rectangle.top + rectangle.height());
}

Solution 14 - Android

The height of the bottom Navigation bar is 48dp (in both portrait and landscape mode) and is 42dp when the bar is placed vertically.

Solution 15 - Android

Combining the answer from @egis and others - this works well on a variety of devices, tested on Pixel EMU, Samsung S6, Sony Z3, Nexus 4. This code uses the display dimensions to test for availability of nav bar and then uses the actual system nav bar size if present.

/**
 * Calculates the system navigation bar size.
 */

public final class NavigationBarSize {

	private final int systemNavBarHeight;
	@NonNull
	private final Point navBarSize;

	public NavigationBarSize(@NonNull Context context) {
		Resources resources = context.getResources();
		int displayOrientation = resources.getConfiguration().orientation;
		final String name;
		switch (displayOrientation) {
			case Configuration.ORIENTATION_PORTRAIT:
				name = "navigation_bar_height";
				break;
			default:
				name = "navigation_bar_height_landscape";
		}
		int id = resources.getIdentifier(name, "dimen", "android");
		systemNavBarHeight = id > 0 ? resources.getDimensionPixelSize(id) : 0;
		navBarSize = getNavigationBarSize(context);
	}

	public void adjustBottomPadding(@NonNull View view, @DimenRes int defaultHeight) {
		int height = 0;
		if (navBarSize.y > 0) {
			// the device has a nav bar, get the correct size from the system
			height = systemNavBarHeight;
		}
		if (height == 0) {
			// fallback to default
			height = view.getContext().getResources().getDimensionPixelSize(defaultHeight);
		}
		view.setPadding(0, 0, 0, height);
	}

	@NonNull
	private static Point getNavigationBarSize(@NonNull Context context) {
		Point appUsableSize = new Point();
		Point realScreenSize = new Point();
		WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
		if (windowManager != null) {
			Display display = windowManager.getDefaultDisplay();
			display.getSize(appUsableSize);
			display.getRealSize(realScreenSize);
		}
		return new Point(realScreenSize.x - appUsableSize.x, realScreenSize.y - appUsableSize.y);
	}

}

Solution 16 - Android

Here is how I solved this. I made a hideable bottom bar which needed padding depending on if there was a navigation bar or not (capacitive, on-screen or just pre lollipop).


View

setPadding(0, 0, 0, Utils.hasNavBar(getContext()) ? 30 : 0);

Utils.java

public static boolean hasNavBar(Context context) {
    // Kitkat and less shows container above nav bar
    if (android.os.Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
        return false;
    }
    // Emulator
    if (Build.FINGERPRINT.startsWith("generic")) {
        return true;
    }
    boolean hasMenuKey = ViewConfiguration.get(context).hasPermanentMenuKey();
    boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
    boolean hasNoCapacitiveKeys = !hasMenuKey && !hasBackKey;
    Resources resources = context.getResources();
    int id = resources.getIdentifier("config_showNavigationBar", "bool", "android");
    boolean hasOnScreenNavBar = id > 0 && resources.getBoolean(id);
    return hasOnScreenNavBar || hasNoCapacitiveKeys || getNavigationBarHeight(context, true) > 0;
}

public static int getNavigationBarHeight(Context context, boolean skipRequirement) {
    int resourceId = context.getResources().getIdentifier("navigation_bar_height", "dimen", "android");
    if (resourceId > 0 && (skipRequirement || hasNavBar(context))) {
        return context.getResources().getDimensionPixelSize(resourceId);
    }
    return 0;
}

Solution 17 - Android

In my case where I wanted to have something like this:

Screenshot

I had to follow the same thing as suggested by @Mdlc but probably slightly simpler (targeting only >= 21):

    //kotlin
    val windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager
    val realSize = Point()
    windowManager.defaultDisplay.getRealSize(realSize);
    val usableRect = Rect()
    windowManager.defaultDisplay.getRectSize(usableRect)
    Toast.makeText(this, "Usable Screen: " + usableRect + " real:"+realSize, Toast.LENGTH_LONG).show()

    window.decorView.setPadding(usableRect.left, usableRect.top, realSize.x - usableRect.right, realSize.y - usableRect.bottom)

It works on landscape too:

landscape

Edit The above solution does not work correctly in multi-window mode where the usable rectangle is not smaller just due to the navigation bar but also because of custom window size. One thing that I noticed is that in multi-window the navigation bar is not hovering over the app so even with no changes to DecorView padding we have the correct behaviour:

Multi-window with no changes to decor view padding Single-window with no changes to decor view padding

Note the difference between how navigation bar is hovering over the bottom of the app in these to scenarios. Fortunately, this is easy to fix. We can check if app is multi window. The code below also includes the part to calculate and adjust the position of toolbar (full solution: https://stackoverflow.com/a/14213035/477790)

    // kotlin
    // Let the window flow into where window decorations are
    window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN)
    window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS)

    // calculate where the bottom of the page should end up, considering the navigation bar (back buttons, ...)
    val windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager
    val realSize = Point()
    windowManager.defaultDisplay.getRealSize(realSize);
    val usableRect = Rect()
    windowManager.defaultDisplay.getRectSize(usableRect)
    Toast.makeText(this, "Usable Screen: " + usableRect + " real:" + realSize, Toast.LENGTH_LONG).show()

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N || !isInMultiWindowMode) {
        window.decorView.setPadding(usableRect.left, usableRect.top, realSize.x - usableRect.right, realSize.y - usableRect.bottom)
        // move toolbar/appbar further down to where it should be and not to overlap with status bar
        val layoutParams = ConstraintLayout.LayoutParams(appBarLayout.layoutParams as ConstraintLayout.LayoutParams)
        layoutParams.topMargin = getSystemSize(Constants.statusBarHeightKey)
        appBarLayout.layoutParams = layoutParams
    }

Result on Samsung popup mode:

enter image description here

Solution 18 - Android

In case of Samsung S8 none of the above provided methods were giving proper height of navigation bar so I used the KeyboardHeightProvider keyboard height provider android. And it gave me height in negative values and for my layout positioning I adjusted that value in calculations.

Here is KeyboardHeightProvider.java :

import android.app.Activity;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.WindowManager.LayoutParams;
import android.widget.PopupWindow;


/**
 * The keyboard height provider, this class uses a PopupWindow
 * to calculate the window height when the floating keyboard is opened and closed. 
 */
public class KeyboardHeightProvider extends PopupWindow {

    /** The tag for logging purposes */
    private final static String TAG = "sample_KeyboardHeightProvider";

    /** The keyboard height observer */
    private KeyboardHeightObserver observer;

    /** The cached landscape height of the keyboard */
    private int keyboardLandscapeHeight;

    /** The cached portrait height of the keyboard */
    private int keyboardPortraitHeight;

    /** The view that is used to calculate the keyboard height */
    private View popupView;

    /** The parent view */
    private View parentView;

    /** The root activity that uses this KeyboardHeightProvider */
    private Activity activity;

    /** 
     * Construct a new KeyboardHeightProvider
     * 
     * @param activity The parent activity
     */
    public KeyboardHeightProvider(Activity activity) {
		super(activity);
        this.activity = activity;

        LayoutInflater inflator = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
        this.popupView = inflator.inflate(R.layout.popupwindow, null, false);
        setContentView(popupView);

        setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_RESIZE | LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
        setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);

        parentView = activity.findViewById(android.R.id.content);

        setWidth(0);
        setHeight(LayoutParams.MATCH_PARENT);

        popupView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

                @Override
                public void onGlobalLayout() {
                    if (popupView != null) {
                        handleOnGlobalLayout();
                    }
                }
            });
    }

    /**
     * Start the KeyboardHeightProvider, this must be called after the onResume of the Activity.
     * PopupWindows are not allowed to be registered before the onResume has finished
     * of the Activity.
     */
    public void start() {

        if (!isShowing() && parentView.getWindowToken() != null) {
            setBackgroundDrawable(new ColorDrawable(0));
            showAtLocation(parentView, Gravity.NO_GRAVITY, 0, 0);
        }
    }

    /**
     * Close the keyboard height provider, 
     * this provider will not be used anymore.
     */
    public void close() {
        this.observer = null;
        dismiss();
    }

    /** 
     * Set the keyboard height observer to this provider. The 
     * observer will be notified when the keyboard height has changed. 
     * For example when the keyboard is opened or closed.
     * 
     * @param observer The observer to be added to this provider.
     */
    public void setKeyboardHeightObserver(KeyboardHeightObserver observer) {
        this.observer = observer;
    }
   
    /**
     * Get the screen orientation
     *
     * @return the screen orientation
     */
    private int getScreenOrientation() {
        return activity.getResources().getConfiguration().orientation;
    }

    /**
     * Popup window itself is as big as the window of the Activity. 
     * The keyboard can then be calculated by extracting the popup view bottom 
     * from the activity window height. 
     */
    private void handleOnGlobalLayout() {

        Point screenSize = new Point();
        activity.getWindowManager().getDefaultDisplay().getSize(screenSize);

        Rect rect = new Rect();
        popupView.getWindowVisibleDisplayFrame(rect);

        // REMIND, you may like to change this using the fullscreen size of the phone
        // and also using the status bar and navigation bar heights of the phone to calculate
        // the keyboard height. But this worked fine on a Nexus.
        int orientation = getScreenOrientation();
        int keyboardHeight = screenSize.y - rect.bottom;
        
        if (keyboardHeight == 0) {
            notifyKeyboardHeightChanged(0, orientation);
        }
        else if (orientation == Configuration.ORIENTATION_PORTRAIT) {
            this.keyboardPortraitHeight = keyboardHeight; 
            notifyKeyboardHeightChanged(keyboardPortraitHeight, orientation);
        } 
        else {
            this.keyboardLandscapeHeight = keyboardHeight; 
            notifyKeyboardHeightChanged(keyboardLandscapeHeight, orientation);
        }
    }

    /**
     *
     */
    private void notifyKeyboardHeightChanged(int height, int orientation) {
        if (observer != null) {
            observer.onKeyboardHeightChanged(height, orientation);
        }
    }

    public interface KeyboardHeightObserver {
        void onKeyboardHeightChanged(int height, int orientation);
    }
}

popupwindow.xml :

<?xml version="1.0" encoding="utf-8"?>
<View
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/popuplayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/transparent"
    android:orientation="horizontal"/>

Usage in MainActivity

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*

/**
 * Created by nileshdeokar on 22/02/2018.
 */
class MainActivity : AppCompatActivity() , KeyboardHeightProvider.KeyboardHeightObserver  {

    private lateinit var keyboardHeightProvider : KeyboardHeightProvider


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        keyboardHeightProvider = KeyboardHeightProvider(this)
        parentActivityView.post { keyboardHeightProvider?.start() }
    }
    
    override fun onKeyboardHeightChanged(height: Int, orientation: Int) {
        // In case of 18:9 - e.g. Samsung S8
        // here you get the height of the navigation bar as negative value when keyboard is closed.
        // and some positive integer when keyboard is opened.
    }

    public override fun onPause() {
        super.onPause()
        keyboardHeightProvider?.setKeyboardHeightObserver(null)
    }

    public override fun onResume() {
        super.onResume()
        keyboardHeightProvider?.setKeyboardHeightObserver(this)
    }

    public override fun onDestroy() {
        super.onDestroy()
        keyboardHeightProvider?.close()
    }
}

For any further help you can have a look at advanced usage of this here.

Solution 19 - Android

> Simple One-line Solution

As suggested in many of above answers, for example

Simply getting navigation bar height may not be enough. We need to consider whether 1. navigation bar exists, 2. is it on the bottom, or right or left, 3. is app open in multi-window mode.

Fortunately you can easily bypass all the long coding by simply setting android:fitsSystemWindows="true" in your root layout. Android system will automatically take care of adding necessary padding to the root layout to make sure that the child views don't get into the navigation bar or statusbar regions.

There is a simple one line solution

android:fitsSystemWindows="true"

or programatically

findViewById(R.id.your_root_view).setFitsSystemWindows(true);

you may also get root view by

findViewById(android.R.id.content).getRootView();
or
getWindow().getDecorView().findViewById(android.R.id.content)

For more details on getting root-view refer - https://stackoverflow.com/a/4488149/9640177

Solution 20 - Android

My version to handle cutouts + navigation bar

fun View.getCutoutRect(): Rect {
    return when {
        isInEditMode -> {
            val cutout = context.dpToPx(16f).roundToInt()
            Rect(cutout, cutout, cutout, cutout)
        }
        Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> {
            val windowInsets = (context as? AppCompatActivity)?.window?.decorView?.rootWindowInsets ?: run {
                requestLayout()
                return Rect()
            }
            val cutout = WindowInsetsCompat.toWindowInsetsCompat(windowInsets).displayCutout
            val systemBars = WindowInsetsCompat.toWindowInsetsCompat(windowInsets).getInsets(WindowInsetsCompat.Type.systemBars())

            Rect(
                maxOf(cutout?.safeInsetLeft ?: 0, systemBars.left),
                maxOf(cutout?.safeInsetTop ?: 0, systemBars.top),
                maxOf(cutout?.safeInsetRight ?: 0, systemBars.right),
                maxOf(cutout?.safeInsetBottom ?: 0, systemBars.bottom),
            )
        }
        else -> {
            val savedRect = (this.getTag(R.id.view_insets_tag_id) as? Rect) ?: Rect()
            ViewCompat.setOnApplyWindowInsetsListener(this) { v, insets ->
                val cutout = insets.displayCutout
                val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
                val rect = Rect(
                    maxOf(cutout?.safeInsetLeft ?: 0, systemBars.left),
                    maxOf(cutout?.safeInsetTop ?: 0, systemBars.top),
                    maxOf(cutout?.safeInsetRight ?: 0, systemBars.right),
                    maxOf(cutout?.safeInsetBottom ?: 0, systemBars.bottom),
                )
                this.setTag(R.id.view_insets_tag_id, rect)
                if (savedRect != rect) {
                    requestLayout()
                }
                return@setOnApplyWindowInsetsListener insets
            }
            this.requestApplyInsets()
            savedRect
        }
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKevikView Question on Stackoverflow
Solution 1 - AndroidSanketView Answer on Stackoverflow
Solution 2 - AndroidEgisView Answer on Stackoverflow
Solution 3 - AndroidMdlcView Answer on Stackoverflow
Solution 4 - AndroidDanylo VolokhView Answer on Stackoverflow
Solution 5 - AndroidJohnView Answer on Stackoverflow
Solution 6 - AndroidFrancisco DrumondView Answer on Stackoverflow
Solution 7 - AndroidmarcusView Answer on Stackoverflow
Solution 8 - AndroidHaytham AnmarView Answer on Stackoverflow
Solution 9 - AndroidChebyrView Answer on Stackoverflow
Solution 10 - AndroidSino RajView Answer on Stackoverflow
Solution 11 - AndroidSagarView Answer on Stackoverflow
Solution 12 - AndroidAnh DuyView Answer on Stackoverflow
Solution 13 - AndroidMatteoView Answer on Stackoverflow
Solution 14 - AndroidAritra RoyView Answer on Stackoverflow
Solution 15 - AndroidMeanmanView Answer on Stackoverflow
Solution 16 - AndroidJonas BorggrenView Answer on Stackoverflow
Solution 17 - AndroidBakhshiView Answer on Stackoverflow
Solution 18 - AndroidNilesh DeokarView Answer on Stackoverflow
Solution 19 - Androidmayank1513View Answer on Stackoverflow
Solution 20 - AndroidiscariotView Answer on Stackoverflow