Material design is a comprehensive guide for visual, motion, and interaction design across platforms and devices. In this codelab, you'll learn the principles of this design language by building a sample Android app.
* Android devices running Android 2.3.3 (Gingerbread, API Level 10) or higher may be used, however, some material design effects, such as the ripple effect, will not be visible on devices running on Android 4.4 (KitKat) or before.
You can either download all the sample code to your computer...
...or clone the GitHub repository from the command line.
$ git clone https://github.com/googlecodelabs/android-design-library.git
First, let's see what the finished sample app looks like. With the code downloaded, the following instructions describe how to open the completed sample app in Android Studio.
android-design-library
package.1-Base
directory from the sample code folder (Quickstart > Import Project... > 1-Base
).Let's jump right into two key features of material design: Themes and Colors!
Themes let you apply a consistent tone to an app, and developers can choose between dark or light themes (see Figure 1 and Figure 2).
Custom colors can also be defined using theme attributes which are then automatically used by the app for different components e.g colorPrimaryDark for the Status Bar and colorPrimary for the App Bar (see Figure 3 below).
<resources>
<!-- Base application theme. -->
<style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">#3F51B5</item>
<!-- Light Indigo -->
<item name="colorPrimaryDark">#3949AB</item>
<!-- Dark Indigo -->
<item name="colorAccent">#00B0FF</item>
<!-- Blue -->
</style>
<style name="AppTheme" parent="AppTheme.Base"></style>
</resources>
By using attribute parent="Theme.AppCompat.Light.NoActionBar"
will remove ActionBar from app.
The app should now look like this:
In this step you'll create the basic skeleton of an app, which you'll fill in later by adding material design components.
Now you're ready to build on top of the starter project.
activity_main.xml
and MainActivity.java
TextView
and add Toolbar
in activity_main.xml
<RelativeLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<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/ThemeOverlay.AppCompat.Light" />
</RelativeLayout>
android.support.v7.widget.Toolbar
at MainActivity.java and add following codes to add Toolbar to Main screen.public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Adding Toolbar to Main screen
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
For this codelab we are using CoordinatorLayout
as a container for child views such as TabLayout and FloatingActionButton.
activity_main.xml
remove RelativeLayout
and replace with CoordinatorLayout.
Tabs
, we will use TabLayout
. First we will create AppBarLayout
then we will place ToolbarLayout
and TabLayout
within AppBarLayout
. You can see code samples from below.<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<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/ThemeOverlay.AppCompat.Light" />
<android.support.design.widget.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>
android.support.design.widget.TabLayout
.MainActivity.java
and add following codes within onCreate method to create tabs. protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TabLayout tabs = (TabLayout) findViewById(R.id.tabs);
tabs.addTab(tabs.newTab().setText("Tab 1"));
tabs.addTab(tabs.newTab().setText("Tab 2"));
tabs.addTab(tabs.newTab().setText("Tab 3"));
}
The app should now look like this:
Let's add individual views to the tabs so that when you move between tabs you can have different layouts.
ListContentFragment.java
, TileContentFragment.java
and CardContentFragment.java
.MainActivity.java
create a ViewPager
variable and Adapter
.protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Adding Toolbar to Main screen
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
// Setting ViewPager for each Tabs
ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
setupViewPager(viewPager);
// Set Tabs inside Toolbar
TabLayout tabs = (TabLayout) findViewById(R.id.tabs);
tabs.setupWithViewPager(viewPager);
}
// Add Fragments to Tabs
private void setupViewPager(ViewPager viewPager) {
Adapter adapter = new Adapter(getSupportFragmentManager());
adapter.addFragment(new ListContentFragment(), "List");
adapter.addFragment(new TileContentFragment(), "Tile");
adapter.addFragment(new CardContentFragment(), "Card");
viewPager.setAdapter(adapter);
}
static class Adapter extends FragmentPagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public Adapter(FragmentManager manager) {
super(manager);
}
@Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
@Override
public int getCount() {
return mFragmentList.size();
}
public void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
@Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
}
activity_main.xml
add a ViewPager
element outside of AppBarLayout
so that AppBar will stay on top of screen while scrolling the views.</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
item_list.xml
, item_tile.xml
and item_card.xml
under res/layout/
. Take a look at the individual attributes to see what customizations are available.Your app should now look like this:
RecyclerView is a container for displaying large data sets that can be scrolled very efficiently by maintaining a limited number of views. Our data set for this codelab is comprised of empty cards which are pieces of paper that serve as an entry point to more information.
Let's add a RecyclerView to our app:
build.gradle
to use CardView and RecyclerView.dependencies {
compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.android.support:design:23.4.0'
compile 'com.android.support:cardview-v7:23.4.0'
compile 'com.android.support:recyclerview-v7:23.4.0'
}
recycler_view.xml
under the res/layout folder.<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/my_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingBottom="@dimen/md_keylines"
android:paddingTop="@dimen/md_keylines"
android:scrollbars="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
RecyclerView
. Start by extending RecyclerView.ViewHolder
. Replace R.layout.item_tile as appropriate, to match the view used in each fragment.public static class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(LayoutInflater inflater, ViewGroup parent) {
super(inflater.inflate(R.layout.item_tile, parent, false));
}
}
RecyclerView.Adapter
instance, wrapping the ViewHolder
that was just created.public static class ContentAdapter extends RecyclerView.Adapter<ViewHolder> {
// Set numbers of List in RecyclerView.
private static final int LENGTH = 18;
public ContentAdapter() {
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(parent.getContext()), parent);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
// no-op
}
@Override
public int getItemCount() {
return LENGTH;
}
}
onCreateView()
to use the new ContentAdapter
.TileContentFragment
, let's also take a moment to adjust the padding and tell RecyclerView
to use a GridLayout
— this will make things look nicer, since our tiles will be small.public class TileContentFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
RecyclerView recyclerView = (RecyclerView) inflater.inflate(
R.layout.recycler_view, container, false);
ContentAdapter adapter = new ContentAdapter();
recyclerView.setAdapter(adapter);
recyclerView.setHasFixedSize(true);
// Set padding for Tiles (not needed for Cards/Lists!)
int tilePadding = getResources().getDimensionPixelSize(R.dimen.tile_padding);
recyclerView.setPadding(tilePadding, tilePadding, tilePadding, tilePadding);
recyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 2));
return recyclerView;
}
If you need help, take a look at TileContentFragment.java
, ListContentFragment.java
, and CardContentFragment.java
. Your app should now look like this:
item_list.xml
, item_tile.xml
to include an image.item_card.xml
, wrapping our RelativeLayout inside a CardView so that we can get the shadows and rounded-corners typical of a card. (On Lollipop or newer devices, shadows is accomplished using Android's native elevation
property. On older devices, the support library simulates the effect using drawables.)Your app should now look like this:
In this step you'll learn how to use VectorDrawable by using Support Library support-vector-drawable. You'll be able to use VectorDrawableCompat back to API 7 device. By using vector assets app only need single vector assets for multiple device screen.
Add following new attribute
// Gradle Plugin 2.0+
android {
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
}
To import vector assets to Android Studio, press CTRL
+ Enter
at drawable
directory and choose New -> Vector Asset
.
For this codelab we will import favorite
and share
vector assets to style cards
.
Once you import vector assets, you can now use app:srcCompat
instead of android:src
to render images within cards
by using vector support library.
<ImageButton
android:id="@+id/share_button"
android:layout_width="@dimen/cards_button_width"
android:layout_height="@dimen/cards_button_height"
android:layout_marginRight="@dimen/md_keylines"
app:srcCompat="@drawable/ic_share"
android:layout_below="@+id/card_text"
android:layout_alignParentRight="true"
style="?android:attr/borderlessButtonStyle"
android:tint="@color/button_grey" />
Your app should now look like this:
You can notice that we have favorite
and share
icons within card.
The navigation drawer slides in from the left. It is a common pattern found in Google apps and follows the keylines and metrics for lists.
menu_navigation.xml
defining the navigation items under res/menu
folder.<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="single">
<item
android:icon="@drawable/ic_home_black_24dp"
android:tint="@color/button_grey"
android:title="One" />
<item
android:icon="@drawable/ic_favorite_black_24dp"
android:tint="@color/button_grey"
android:title="Two" />
<item
android:icon="@drawable/ic_bookmark_border_black_24dp"
android:tint="@color/button_grey"
android:title="Three" />
</group>
</menu>
navheader.xml
defining a Navigation Drawer material under the res/layout/
folder.<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/navheader_height"
android:background="?attr/colorPrimaryDark"
android:orientation="vertical"
android:padding="@dimen/md_keylines">
</LinearLayout>
DrawerLayout
which enables interactive drawer views to be pulled out from the edge of the window.<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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
NavigationView
outside the CoordinatorLayout
.<android.support.design.widget.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="@layout/navheader"
app:menu="@menu/menu_navigation" />
private DrawerLayout mDrawerLayout;
onCreate
method of MainActivity.java
to set the navigation drawer.// Create Navigation drawer and inflate layout
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer);
// Adding menu icon to Toolbar
ActionBar supportActionBar = getSupportActionBar();
if (supportActionBar != null) {
supportActionBar.setHomeAsUpIndicator(R.drawable.ic_menu_white_24dp);
supportActionBar.setDisplayHomeAsUpEnabled(true);
}
// Set behavior of Navigation drawer
navigationView.setNavigationItemSelectedListener(
new NavigationView.OnNavigationItemSelectedListener() {
// This method will trigger on item Click of navigation menu
@Override
public boolean onNavigationItemSelected(MenuItem menuItem) {
// Set item in checked state
menuItem.setChecked(true);
// TODO: handle navigation
// Closing drawer on item click
mDrawerLayout.closeDrawers();
return true;
}
});
mDrawerLayout.openDrawer(GravityCompat.START);
to MainActivity.java
to make the navigation drawer move when you touch the menu.@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
} else if (id == android.R.id.home) {
mDrawerLayout.openDrawer(GravityCompat.START);
}
return super.onOptionsItemSelected(item);
}
Floating action buttons are used for a promoted action and are distinguished by a circled icon floating above the UI. Let's create a FAB that triggers a SnackBar which provides lightweight feedback about an operation by showing a brief message.
activity_main.xml
add a FloatingActionButton
to the end of the CoordinatorLayout
.MainActivity.java
, add a Listener to the FAB that creates a SnackBar onclick<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|bottom"
android:layout_marginBottom="@dimen/md_keylines"
android:layout_marginRight="@dimen/md_keylines"
android:src="@drawable/ic_add_white_24dp" />
// Adding Floating Action Button to bottom right of main view
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Snackbar.make(v, "Hello Snackbar!",
Snackbar.LENGTH_LONG).show();
}
});
values-v21
folder, update styles.xml
to create system bar transparent for device running Android 5 and above.<resources>
<style name="AppTheme" parent="AppTheme.Base">
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
</resources>
CardContentFragment.java
file add methods when buttons within card pressed shows snackbar.Your app should now look like this:
Use an Intent
to allow users to move between cards and detail views. Let's create a detail view in our app:
DetailActivity.java
and activity_detail.xml
.Intent
within each of ListContentFragment.java
, TileContentFragment.java
, and CardContentFragment.java
so that each item has a link to the detailed view.public static class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(LayoutInflater inflater, ViewGroup parent) {
super(inflater.inflate(R.layout.item_list, parent, false));
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Context context = v.getContext();
Intent intent = new Intent(context, DetailActivity.class);
context.startActivity(intent);
}
});
}
}
activity
to the AndroidManifest.xml
file.<activity
android:name=".DetailActivity"
android:parentActivityName=".MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".MainActivity" />
</activity>
A Collapsing Toolbar provides visual transitions by collapsing a toolbar as the user scrolls down the app. Let's go ahead and add a collapsing toolbar:
activity_detail.xml
, add an AppBarLayout
and CollapsingToolbarLayout
within CoordinatorLayout.
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="@dimen/app_bar_height"
android:fitsSystemWindows="true"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:theme="@style/ThemeOverlay.AppCompat.Dark"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleMarginEnd="@dimen/article_keylines"
app:expandedTitleMarginStart="@dimen/md_keylines"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/paris"
android:fitsSystemWindows="true"
android:scaleType="centerCrop"
app:layout_collapseMode="parallax" />
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
DetailActivity.java
set the title of the collapsing toolbar's detail view.// Set Collapsing Toolbar layout to the screen
CollapsingToolbarLayout collapsingToolbar =
(CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar);
// Set title of Detail page
collapsingToolbar.setTitle(getString(R.string.item_title));
Congratulations, you're finished! You're now ready to create a material design app.
When you have a spare moment, we'd really appreciate if you could fill out some feedback on your codelab experience. We'll use this information to iterate and improve the codelab over time.
Now it's time to add material design to your own app.