I’m a massive fan of material design. Everything about it provides a strong feeling of consistency between applications and as a whole makes them both easier and more aesthetically pleasing to use. Google I/O 2015 saw the introduction of some great new assets to the world of Android — including the new Design Support Library. With the introduction of this, there’s now no excuse not to follow the Material Design Guidelines provided by Google.
Let’s take a look at these new out-of-the-box components that we now have available to us.
Mostly inheriting the same methods and attributes as the Toast component, the Snackbar is a new component that allows us to show a quick message to the user at the bottom of the screen. Once animated in, the user can either interact with the Action (if one has been set) or dismiss the Snackbar by swiping it off the screen. If neither of these occurs, then it’ll automatically animate off of the screen after the given timeout.
For developers, it’s also dead easy to implement with a few lines of code (you don’t want to break the line limit now do you…):
Snackbar.make(mDrawerLayout, "Your message", Snackbar.LENGTH_SHORT) .setAction(getString(R.string.text_undo), this) .show();
Note: Whilst you can only display a single Snackbar at any given time, it is possible to ‘queue’ multiple Snackbars to be shown in the order that the show() method is called on each instance.
Floating Action Button
A Floating Action Button (FAB) is a standard component for prompting interaction with a specific action, e.g. adding a new item to a list. It can now be implemented easily throughout our applications, without the use of third-party libraries that were previously an option.
The button can be used as one of two sizes, these are:
Normal (56dp) — This size should be used in most situations.
Mini (40dp) — Should only be used when there is a need for visual continuity with other components displayed on the screen.
By default, the FAB will use the application theme accent colour for its background. However, we can easily change the background colour of an individual button, along with many other attributes that we may wish to alter:
- fabSize – Used to set the size of the button (‘normal’ or ‘mini’)
- backgroundTint – Used to set the background colour of this instance
- borderWidth -Used to give the button a border
- rippleColor – Used to set the colour of the ripple effect when pressed
- src – Used to set the icon displayed within the FAB
Again, this is super easy to add to our layout file:
<android.support.design.widget.FloatingActionButton android:id=”@+id/fab_normal” android:layout_width=”wrap_content” android:layout_height=”wrap_content” android:src=”@drawable/ic_plus” app:fabSize=”normal” />
EditText Floating Labels
The new TextInputLayout allows us to wrap EditText views in order to display floating labels above the EditText field. When an EditText has focus, the assigned hint will ‘float’ above the view to the top-left hand side. This is useful as it still provides context to the user whilst data is being input.
To implement this we just wrap our EditText in the TextInput Layout:
<android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <EditText android:id="@+id/edit_text_email" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="textEmailAddress" android:hint="@string/hint_email" /> </android.support.design.widget.TextInputLayout>
Error Messages are also supported, which can be shown by simply adding the following to our class:
Note: Setting the error message after setting the ‘errorEnabled’ flag will ensure the size of the layout doesn’t alter when the error message is shown.
The Navigation Drawer is a commonly used component in modern applications, implementing it over and over was never a quick process – until now! The new NavigationView component can simply be placed within our DrawerLayout (see code example below) and display our navigation items from the referenced menu resource.
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true"> <FrameLayout android:id="@+id/main_content_frame" android:layout_width="match_parent" android:layout_height="match_parent" /> <android.support.design.widget.NavigationView android:id="@+id/navigation_view" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="start" app:headerLayout="@layout/navigation_header" app:menu="@menu/drawer" /> </android.support.v4.widget.DrawerLayout>
This view supports two key attributes:
The optional headerLayout attribute is used to declare a layout to be used for the header section of the Drawer. This is the space shown above our navigational items, a common use is a profile section header.
The menu attribute is used to declare the menu resource to be used for the navigation items in the drawer. It is also possible to call inflateMenu() to inflate a menu programmatically.
As shown above, there are two approaches for our NavigationView menus. The first approach is achieved by using a standard set of grouped checkable items:
<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity"> <group android:checkableBehavior="single"> <item android:id="@+id/navigation_item_1" android:checked="true" android:icon="@drawable/ic_android" android:title="@string/navigation_item_1" /> <item android:id="@+id/navigation_item_2" android:icon="@drawable/ic_android" android:title="@string/navigation_item_2" /> </group> </menu>
Here the items are simply shown in a vertical list, no subheadings are displayed and the items all belong in the same group.
The second is similar, but this time we can use a sub-header for our sets of navigation items. As seen below, I have applied a sub-header to the set of items in my menu resource:
<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity"> <group android:checkableBehavior="single"> <item android:id="@+id/navigation_subheader" android:title="@string/nav_header"> <menu> <!-- Menu items go here --> </menu> </item> </group> </menu>
This allows us to separate our menu items by the use of a header. This can be useful if menu items are grouped into specific sets, allowing some form of separation on screen.
It is also possible for us to add menu items programmatically, we just have to call getMenu() to retrieve our menu and then items can be added to that instance.
There are several other important attributes that we can easily change, these are:
- itemBackground — Used to set the background resource of the menu items
- itemIconTint — Used to apply a tint to the icons
- itemTextColor — Used to set the text color of the menu items
In order to capture click events on our menu items we just need to set an OnNavigationItemSelectedListener, this will allow us to react to any touch events that take place on our menu.
Note: For API21+, the NavigationView automatically takes care of scrim protection for the status bar.
The TabLayout is another new component that’ll make our lives easier by providing a scrollable tab bar component for use in for applications. There are several ways in which we can use these:
To begin with, we need to add the TabLayout to our layout:
<android.support.design.widget.TabLayout android:id="@+id/sliding_tabs" android:layout_width="match_parent" android:layout_height="wrap_content" app:tabMode="fixed" app:tabGravity="fill" />
Once done, there are several important attributes here that we can set to adjust the appearance of our TabLayout:
- tabMode – This sets the mode to use for the TabLayout. This can either be fixed (all tabs are shown concurrently) or scrollable (show a subset of tabs that can be scrolled through)
- tabGravity – This sets the Gravity of the tabs, which can be either fill (distribute all available space between individual tabs) or centre (position tabs in the center of the TabLayout)
- setText() – This method is used to set the text to be displayed on the tab
- setIcon() – This method is used to set the icon to be displayed on the tab
We also have access to several different kinds of listeners that we can set when using the TabLayout view:
- OnTabSelectedListener – This can be set to listen for changes on a tabs selected state
- TabLayoutOnPageChangeListener – Contains the call backs to the corresponding TabLayout, it handles the syncing of tabs selected states. It can be set programmatically without removing the existing listener as the TabLayout is stored weakly within the class
- ViewPagerOnTabSelectedListener – Contains the callbacks to the corresponding ViewPager, again this handles the syncing of tabs selected states.
Once the view has been added to our layout the implementation is simple, you just need to implement the setupWithViewPager() method to attach the TabLayout to your viewpager:
ViewPager pager = (ViewPager) rootView.findViewById(R.id.viewPager); pager.setAdapter(new MyPagerAdapter(getActivity().getSupportFragmentManager())); TabLayout tabLayout = (TabLayout) rootView.findViewById(R.id.sliding_tabs);
tabLayout.addTab(tabLayout.newTab().setText("Tab One")); tabLayout.addTab(tabLayout.newTab().setText("Tab Two")); tabLayout.addTab(tabLayout.newTab().setText("Tab Three")); tabLayout.setupWithViewPager(pager);
Note: Tabs should be added either as above or from within a ViewPager. Using setTabsFromPagerAdapter() will cause only tabs that have been added inside of your PagerAdapter to be used, removing any that have been added using the addTab() method.
The CoordinatorLayout builds on-top of the motion effects already provided by adding the ability to transition views based on the motion of others.
To ensure the features of this component work as intended, please ensure that your other support library dependencies are using the latest version. I needed to update RecyclerView to version 22.2.0 in order for it to work properly with some the design support library features.
This layout adds two new attributes that can be used to control the anchoring of a view in relation to other views on screen.
- layout_anchor — Used to anchor the view on the seam (edge) of another view
- layout_anchorGravity — Used to set the gravity to the applied anchor
Floating Action Button
We previously looked at the Snackbar and touched on how this is shown on top of all other UI components. However, we are able to link our FloatingActionButton to our Snackbar so that when the bar is shown it pushes the FAB up, rather than overlapping it.
In order to implement this our FloatingActionBar needs to first be a child of our CoordinatorLayout. Next, you’ll need to ensure that you’ve set the layout_gravity to declare the desired position of our FAB.
<!-- Your other views -->
<android.support.design.widget.FloatingActionButton android:id=”@+id/fab_normal” android:layout_width=”wrap_content” android:layout_height=”wrap_content” android:src=”@drawable/ic_plus” android:layout_gravity="bottom|right" app:fabSize=”normal” />
Finally, when constructing our Snackbar, we just need to pass our CoordinatorLayout as the view parameter, as below:
Snackbar.make(mCoordinator, "Your message", Snackbar.LENGTH_SHORT) .show();
The CoordinatorLayout lets us adapt our layouts based on different scroll events that may take place, allowing us to alter the appearance of our views (such as the Toolbar) when the user scrolls the content on the screen.
In order to achieve this, we first need to set the scroll property within the layout_scrollFlags attribute. This is used to declare whether views should scroll off screen or remain pinned at the top, this property must then be followed by one of the following:
- enterAlways – Used to enable quick return, where the view will become visible when a downward scroll occurs
- enterAlwaysCollapsed – If the corresponding view has a minHeight, then it’ll only enter at this height and expand fully once the scrolling view has reached the top
- exitUntilCollapsed – Used to declare that the view should scroll off the screen until it is collapsed before the content begins to exit
Note: Views that are using the scroll flag must be declared before any views that do not. This will ensure that these declared views all exit from the top, in turn leaving all of the fixed views behind.
As shown in the code below, our recycler view uses the layout_behavior attribute in-order to allow the RecyclerView to work with our Coordinator layout. This means that the layout is able to react to the RecyclerViews scroll events. The code also shows that the Toolbar has its layout_scrollFlags attribute set, meaning that when the RecyclerView is scrolled, its scroll events are captured and our ToolBar will slide out of view. However, we haven’t declared this attribute for our TabLayout, so this will remain pinned at the top of the screen.
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.RecyclerView android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior= "@string/appbar_scrolling_view_behavior" /> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <android.support.v7.widget.Toolbar ... app:layout_scrollFlags="scroll|enterAlways" /> <android.support.design.widget.TabLayout ... />
You can now wrap a Toolbar component with the new CollapsingToolbarLayout, which allows the layout to collapse as the user scrolls the screens content:
<android.support.design.widget.AppBarLayout android:layout_height="192dp" android:layout_width="match_parent">
<android.support.design.widget.CollapsingToolbarLayout android:layout_width="match_parent" android:layout_height="match_parent" app:layout_scrollFlags="scroll|exitUntilCollapsed">
<android.support.v7.widget.Toolbar android:layout_height="?attr/actionBarSize" android:layout_width="match_parent" app:layout_collapseMode="pin" />
When using this component, the layout_collapseMode attribute needs to be set, this can be one of two options.
- Pin – Setting the collapseMode to pin will cause the toolbar to remain pinned at the top of the screen once the CollapsingToolbarLayout has been fully collapsed.
- Parallax – Using the parallax mode will allow the content (e.g the image used within an ImageView) to translate vertically whilst the CollapsingToolbarLayout is collapsing. Setting the optional layout_collapseParallaxMultiplier attribute when using parallax gives control over the translation multiplier on the transition
Another great thing about both of these approaches is that calling setText() directly on the CollapsingToolbarLayout will cause the text size to automatically start larger, shrinking to a smaller size once the CollapsingToolbarLayout has fully collapsed.
It doesn’t end there! You can also define a Behaviour for custom views, allowing callbacks to be received upon onDependentViewChanged() being called. This also allows for better handling of touch events, gestures and dependencies between child views.
So what are you waiting for? Add the library to your dependencies and get cracking!