2

I'm trying to create a VideoPlayer. I'd like the user to have an immersive experience, with the option to use the NavigationBar upon user tap.

void showNavBar() {
    activity.getWindow().getDecorView().setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_IMMERSIVE
}

void hideNavBar() {
    activity.getWindow().getDecorView().setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION 
                | View.SYSTEM_UI_FLAG_FULLSCREEN
                | View.SYSTEM_UI_FLAG_IMMERSIVE
}

this is great, but now I want a ProgressBar. This ProgressBar will only display when the NavigationBar is visible. The problem now, is the NavigationBar covers the end of the ProgressBar. To solve this problem, I'd like the ProgressBar to fill the screen as if the NavBar were present, but still retain the ability to hide the NavigationBar (thus hiding the ProgressBar in the process).

The YouTube app has a great example of this:

YouTube screenshot showing <code>NavigationBar</code> does not cover UI component Notice the ProgressBar understands where to stop in the bottom right corner. How do I get my ProgressBar to do the same?

sudocoder
  • 1,164
  • 1
  • 13
  • 26

1 Answers1

0

This will offset your view to account for the NavBar. It's a lengthy response to account for the fragmented behavior of NavBar on different devices.

private static final int ACCOMMODATE_RIGHT = 0b01;
private static final int ACCOMMODATE_BOTTOM = 0b10;

private void prepareNavBar() {
    // the progressBar is a good example to use ACCOMODATE_RIGHT | ACCOMODATE_BOTTOM because it touches the bottom and right sides
    setViewToAccommodateNavBar((FrameLayout.LayoutParams) footerView.getLayoutParams(), ACCOMMODATE_RIGHT | ACCOMMODATE_BOTTOM);

    // the header in the screenshot is a good example to use ACCOMODATE_RIGHT because it only touches the right side
    setViewToAccommodateNavBar((FrameLayout.LayoutParams) headerView.getLayoutParams(), ACCOMMODATE_RIGHT);
}

// assumes FrameLayout parent, you can easily extend for other ViewGroups
private void setViewToAccommodateNavBar(FrameLayout.LayoutParams layoutParams, int accommodatingFlag) {
    Activity activity = getActivity();

    // if navBar does not exist, then we are done because the margins do not need change.
    if (!hasNavBar(activity))
        return;

    // extract flags specified by caller
    boolean accommodateRight = (accommodatingFlag & ACCOMMODATE_RIGHT) != 0;
    boolean accommodateBottom = (accommodatingFlag & ACCOMMODATE_BOTTOM) != 0;

    // these are the only margins that will be affected (NavBar only appears on the bottom or the right)
    int rightMargin = layoutParams.rightMargin;
    int bottomMargin = layoutParams.bottomMargin;

    // if NavBar is on the bottom, then alter bottomMargin.  otherwise alter rightMargin
    if (isNavBarOnBottom(mContext)) {
        if (!accommodateBottom)
            return;

        boolean isPortrait = mContext.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
        String navBarHeightIdName = isPortrait ? "navigation_bar_height" : "navigation_bar_height_landscape";


        // it's not public so we resort to reflection.
        int resourceId = mContext.getResources().getIdentifier(navBarHeightIdName, "dimen", "android");
        if (resourceId > 0) {
            bottomMargin += mContext.getResources().getDimensionPixelSize(resourceId);
        }
    } else {
        if (!accommodateRight)
            return;

        int resourceId = mContext.getResources().getIdentifier("navigation_bar_width", "dimen", "android");
        if (resourceId > 0) {
            rightMargin += mContext.getResources().getDimensionPixelSize(resourceId);
        }
    }

    // finally, set the margins of your layout.
    layoutParams.setMargins(layoutParams.leftMargin, layoutParams.topMargin, rightMargin, bottomMargin);
}

// http://stackoverflow.com/a/30035735/1895452  (my version is slightly modified.)
public static boolean isNavBarOnBottom(Activity activity) {
    Point size = new Point();
    activity.getWindowManager().getDefaultDisplay().getSize(size);

    if ((size.x == size.y)) return true;
    if (activity.getResources().getConfiguration().smallestScreenWidthDp >= 600)) return true;

    return size.x < size.y;
}

// http://stackoverflow.com/questions/16092431/check-for-navigation-bar
public static boolean hasNavBar() {
    boolean hasMenuKey = ViewConfiguration.get(context).hasPermanentMenuKey();
    boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
    return !hasMenuKey && !hasBackKey
}

In summary, first determine your view's navBar accommodations. You can do this by asking yourself, 'does the view touch the right or bottom edge of the screen?' and use the flags accordingly. Then call setViewToAccommodateNavBar. This will add margins to your view's parent taking into account devices with NavBars on either the right or bottom, as well as devices that do not have a NavBar.

prepareNavBar() should be called before the views get drawn eg: onCreate or onCreateView

sudocoder
  • 1,164
  • 1
  • 13
  • 26