Changing my pace of steady Xamarin content to go to my roots of native mobile development. This time, we’ll look at implementing the latest control provided by Google and Material Design – The BottomNavigationView
.
Resources
- Material Design Guidelines
- Code documentation
- Example of moving from
TabLayout
toBottomNavigationView
from my Github - Third Party more robust solution / control by Roughike
Aren’t These Just Tabs?
I mean… yeah, but… it’s new and cool! Google finally realized that stretching to the top of the app can be annoying.
This new control is also a little different from the TabLayout
we all know in love from Material Design and Android development in that it is limited to 5 maximum tab items and does not support scrolling. It’s meant to act as top level or sibling level navigation as long as all items are of equal importance in the context of the application/current view. It is also nice to give some variety to our applications navigation scheme; larger apps with many tabbed views can become overwhelming, so tossing something new is relieving to our users.
Code
There are 3 major components to setting up a view with a BottomNavigationView
.
- First, we need to create a
menu
resource for our navigation items. - Then we need to create, style, and set up our
BottomNavigationView
in our layout. - Lastly, add listeners for when an item is selected in our
BottomNavigationView
and make sure it fits the experience expectation defined in Material Design.
Create a Menu
In our example, we will be building an application for viewing adoptable puppies. Each navigation item will be a different set of these puppies by categorizing them. Let’s create a menu for “all”, “big”, “small”, “trained”, and “active” as categories for our puppies:
res/menu/bottom_bar_menu.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/all_puppies" android:title="@string/action_all" android:icon="@drawable/ic_home_white_24dp" /> <item android:id="@+id/big_puppies" android:title="@string/action_big" android:icon="@drawable/ic_dog_white_24dp" /> <item android:id="@+id/small_puppies" android:title="@string/action_small" android:icon="@drawable/ic_small_dog_white_24dp" /> <item android:id="@+id/trained_puppies" android:title="@string/action_trained" android:icon="@drawable/ic_trained_white_24dp" /> <item android:id="@+id/active_puppies" android:title="@string/action_active" android:icon="@drawable/ic_active_white_24dp" /> </menu>
With our menu, we can create our layout.
Updating the Layout
In our example, we are moving from a TabLayout
with a ViewPager
. However, the Material Design documentation for the BottomNavigationView
states that it should NOT be used with side-swiping actions such as a ViewPager
. Let’s replace that ViewPager
with a FrameLayout
that will be used to swap our active Fragment
and also remove the TabLayout
that is being replaced by the BottomNavigationView
:
res/layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/main_content" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" tools:context="com.suavepirate.bottomnavigationpuppies.activities.MainActivity"> <android.support.design.widget.AppBarLayout android:id="@+id/appbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingTop="@dimen/appbar_padding_top" android:theme="@style/AppTheme.AppBarOverlay"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:layout_scrollFlags="scroll|enterAlways" app:popupTheme="@style/AppTheme.PopupOverlay"> </android.support.v7.widget.Toolbar> </android.support.design.widget.AppBarLayout> <FrameLayout android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" ></FrameLayout> <android.support.design.widget.BottomNavigationView android:id="@+id/bottombar" android:layout_width="match_parent" android:layout_height="56dp" android:layout_gravity="bottom|fill_horizontal|start" app:menu="@menu/bottom_bar_menu" android:background="@android:color/white" app:elevation="8dp"/> </android.support.design.widget.CoordinatorLayout>
It’s important to layout the BottomNavigationView
at the bottom of the page as well as give it a solid background and elevation. Also, notice how we apply our menu
we created to the view by setting app:menu="@menu/bottom_bar_men"
.
With our layout set, let’s wire up listeners to update the current Fragment
based on the selected navigation item.
Setting Up Listeners
In our MainActivity.java we can implement the BottomNavigationView.OnNavigationItemSelectedListener
interface and override the onNavigationItemSelected
method:
MainActivity.java
// imports public class MainActivity extends AppCompatActivity implements BottomNavigationView.OnNavigationItemSelectedListener { private PageAdapter mSectionsPagerAdapter; private FrameLayout mContainer; private BottomNavigationView mBottomBar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); // Create the adapter that will return a fragment for each of puppy types mSectionsPagerAdapter = new PageAdapter(getSupportFragmentManager(), this); // Set up the ViewPager with the sections adapter. mContainer = (FrameLayout) findViewById(R.id.container); // set up the first Fragment FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); ft.add(R.id.container, mSectionsPagerAdapter.getItem(0), "CURRENT_PAGE"); ft.commit(); // set up the bottom bar and listener mBottomBar = (BottomNavigationView)findViewById(R.id.bottombar); mBottomBar.setOnNavigationItemSelectedListener(this); } // Handles when an item is selected to update the fragment container @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); ft.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out); switch(item.getItemId()){ case R.id.all_puppies: ft.replace(R.id.container, mSectionsPagerAdapter.getItem(0)); break; case R.id.big_puppies: ft.replace(R.id.container, mSectionsPagerAdapter.getItem(1)); break; case R.id.small_puppies: ft.replace(R.id.container, mSectionsPagerAdapter.getItem(2)); break; case R.id.trained_puppies: ft.replace(R.id.container, mSectionsPagerAdapter.getItem(3)); break; case R.id.active_puppies: ft.replace(R.id.container, mSectionsPagerAdapter.getItem(4)); break; } ft.commit(); return true; } }
Now with all of this, we are able to switch the current Fragment
with a fade in and out animation when the selected navigation item is updated. That means our BottomNavigationView
is implemented and ready to go!
If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!
Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.
How about Xamarin.Forms?
LikeLike
Sorry this post isn’t about Xamarin. Xamarin.Forms doesn’t support the version of the Android Support Design Library that has the BottomNavigationView yet. You can use a wrapper of Roughikes BottomBar library with a custom renderer. I have an example of that coming soon on my github.
LikeLike