Bottom Navigation Behavior

Material Design guidelines have been updated with Bottom navigation component, which serves for quickly navigating between sections of the app. According to the guideline this navigation component must have 3 to 5 navigational items, otherwise Navigation Drawer is advised.

Layout

This is the most easy part, TabLayout without tab selection indicator. Unfortunately, the guidelines do not explicitly say that tab strip should not be used when an item is selected (See Style) .

<android.support.design.widget.CoordinatorLayout ...>
<android.support.v4.widget.NestedScrollView...>
<TextView ... />
</android.support.v4.widget.NestedScrollView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="@android:color/background_dark"
android:elevation="@dimen/bottom_navigation_elevation"
android:orientation="vertical"
app:layout_behavior="@string/bottom_navigation_behavior"
app:tabLayoutId="@+id/tabs">

<android.support.design.widget.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
app:tabIndicatorColor="@android:color/transparent" />

<View
android:layout_width="match_parent"
android:layout_height="@dimen/navigation_bar_height"
android:background="@color/colorPrimaryDark" />
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>

The LinearLayout holds our TabLayout and a placeholder view that is behind the navigation bar (~48dp) and according to the design guidelines it should have 8dp elevation.

Setting tabLayoutId is optional, I used it only to apply alpha animation to the tab views.

Behavior

I will not cover the case what should happen when a navigation item is tapped, but only scrolling behavior.

When the current top-level view has a scrolling content, the bottom navigation should hide when it is scrolled down and retract back when the content is scrolled up. For this purpose, we need to be aware of the direction of the scroll. VerticalScrollingBehavior is a generic layout behavior that it is somehow an extension to the CoordinatorLayout.Behavior that dispatches events on for direction of scrolling.

Now lets translate bottom navigation behavior to code:
1. The case when we scroll by direct interaction with the nested scrollview

@Override
public void onDirectionNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, int dy, int[] consumed, @ScrollDirection int scrollDirection) {
handleDirection(child, scrollDirection);
}

2. The case when we interact by flinging

@Override
protected boolean onNestedDirectionFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX, float velocityY, @ScrollDirection int scrollDirection) {
handleDirection(child, scrollDirection);
return true;
}

As you can see, both cases are handled by one method what isn’t a rocket science:

private void handleDirection(V child, int scrollDirection) {
if (scrollDirection == ScrollDirection.SCROLL_DIRECTION_DOWN && hidden) {
hidden = false;
animateOffset(child, 0);
} else if (scrollDirection == ScrollDirection.SCROLL_DIRECTION_UP && !hidden) {
hidden = true;
animateOffset(child, child.getHeight());
}
}
private void animateOffset(final V child, final int offset) {
ensureOrCancelAnimator(child);
mTranslatenimator.translationY(offset).start();
//...
}

This method simply calls a translation animation to hide or show the bottom navigation component, based on the scrolling direction.

Code

Head to this gist

Demo