Android fundamentals 04.4: User navigation

1. Welcome

Introduction

In the early stages of developing an app, you should determine the path you want users to take through your app to do each task. (The tasks are things like placing an order or browsing content.) Each path enables users to navigate across, into, and out of the tasks and pieces of content within the app.

In this practical, you learn how to add an Up button (a left-facing arrow) to the app bar, as shown below.

Up button for up navigation

The Up button is always used to navigate to the parent screen in the hierarchy. It differs from the Back button (the triangle at the bottom of the screen), which provides navigation to whatever screen the user last viewed.

This practical also introduces tab navigation, in which tabs appear across the top of a screen, providing navigation to other screens. Tab navigation is a popular way to create lateral navigation from one child screen to a sibling child screen, as shown in the figure below.

Using tabs to navigate sibling screens

In the figure above:

  1. Lateral navigation from one category screen (Top Stories, Tech News, and Cooking) to another
  2. Lateral navigation from one story screen (Story) to another

With the tabs, the user can navigate to and from the sibling screens without navigating up to the parent screen. Tabs can also provide navigation to and from stories, which are sibling screens under the Top Stories parent.

Tabs are most appropriate for four or fewer sibling screens. To see a different screen, the user can tap a tab or swipe left or right.

What you should already know

You should be able to:

  • Create and run apps in Android Studio.
  • Create and edit UI elements using the layout editor.
  • Edit XML layout code, and access elements from your Java code.
  • Add menu items and icons to the options menu in the app bar.

What you'll learn

  • How to add the Up button to the app bar.
  • How to set up an app with tab navigation and swipe views.

What you'll do

  • Continue adding features to the Droid Cafe project from the previous practical.
  • Provide the Up button in the app bar to navigate up to the parent Activity.
  • Create a new app with tabs for navigating Activity screens that can also be swiped.

2. App overview

In the previous practical on using the options menu, you worked on an app called Droid Cafe that was created using the Basic Activity template. This template provides an app bar at the top of the screen. You will learn how to add an Up button (a left-facing arrow) to the app bar for up navigation from the second Activity (OrderActivity) to the parent Activity (MainActivity). This will complete the Droid Cafe app.

To start the project from where you left off in the previous practical, download the Android Studio project DroidCafeOptions.

Up navigation in the app bar

You will also create an app for tab navigation that shows three tabs below the app bar to navigate to sibling screens. When the user taps a tab, the screen shows a content screen, depending on which tab the user tapped. The user can also swipe left and right to visit the content screens. The ViewPager class automatically handles user swipes to screens or View elements.

Tab navigation

3. Task 1: Add an Up button for ancestral navigation

Your app should make it easy for users to find their way back to the app's main screen, which is usually the parent Activity. One way to do this is to provide an Up button in the app bar for each Activity that is a child of the parent Activity.

The Up button provides ancestral "up" navigation, enabling the user to go up from a child page to the parent page. The Up button is the left-facing arrow on the left side of the app bar, as shown on the left side of the figure below.

When the user touches the Up button, the app navigates to the parent Activity. The diagram on the right side of the figure below shows how the Up button is used to navigate within an app based on the hierarchical relationships between screens.

The Up button and screen hierarchy

In the figure above:

  1. Navigating from the first-level siblings to the parent.
  2. Navigating from second-level siblings to the first-level child screen acting as a parent screen.

As you learned previously, when adding activities to an app, you can add Up-button navigation to a child Activity such as OrderActivity by declaring the parent of the Activity to be MainActivity in the AndroidManifest.xml file. You can also set the android:label attribute for a title for the Activity screen, such as "Order Activity". Follow these steps:

  1. If you don't already have the Droid Cafe app open from the previous practical, download the Android Studio project DroidCafeOptions and open the project.
  2. Open AndroidManifest.xml and change the Activity element for OrderActivity to the following:
<activity android:name="com.example.android.droidcafeinput.OrderActivity"
    android:label="Order Activity"
    android:parentActivityName=".MainActivity">
    <meta-data android:name="android.support.PARENT_ACTIVITY"
        android:value=".MainActivity"/>
</activity>
  1. Extract the android:label value "Order Activity" to a string resource named title_activity_order.
  2. Run the app.

The Order Activity screen now includes the Up button (highlighted in the figure below) in the app bar to navigate back to the parent Activity.

Up navigation in the app bar

Task 1 solution code

Android Studio project: DroidCafeOptionsUp

4. Task 2: Use tab navigation with swipe views

With lateral navigation, you enable the user to go from one sibling to another (at the same level in a multitier hierarchy). For example, if your app provides several categories of stories (such as Top Stories, Tech News, and Cooking, as shown in the figure below), you would want to provide your users the ability to navigate from one category to the next, without having to navigate back up to the parent screen. Another example of lateral navigation is the ability to swipe left or right in a Gmail conversation to view a newer or older one in the same Inbox.

Lateral navigation with tabs

In the figure above:

  1. Lateral navigation from one category screen to another
  2. Lateral navigation from one story screen to another

You can implement lateral navigation with tabs that represent each screen. Tabs appear across the top of a screen, as shown on the left side of the figure above, in order to provide navigation to other screens. Tab navigation is a very popular solution for lateral navigation from one child screen to another child screen that is a sibling—in the same position in the hierarchy and sharing the same parent screen. Tab navigation is often combined with the ability to swipe child screens left-to-right and right-to-left.

The primary class used for displaying tabs is TabLayout in the Android Design Support Library. It provides a horizontal layout to display tabs. You can show the tabs below the app bar, and use the PagerAdapter class to populate screens "pages" inside of a ViewPager. ViewPager is a layout manager that lets the user flip left and right through screens. This is a common pattern for presenting different screens of content within an Activity—use an adapter to fill the content screen to show in the Activity, and a layout manager that changes the content screens depending on which tab is selected.

You implement PagerAdapter to generate the screens that the view shows. ViewPager is most often used in conjunction with Fragment. By using a Fragment, you have a convenient way to manage the lifecycle of a screen "page".

To use classes in the Android Support Library, add com.android.support:design:xx.xx.x (in which xx.xx.x is the newest version) to the build.gradle (Module: app) file.

The following are standard adapters for using fragments with the ViewPager:

  • FragmentPagerAdapter: Designed for navigating between sibling screens (pages) representing a fixed, small number of screens.
  • FragmentStatePagerAdapter: Designed for paging across a collection of screens (pages) for which the number of screens is undetermined. It destroys each Fragment as the user navigates to other screens, minimizing memory usage. The app for this task uses FragmentStatePagerAdapter.

2.1 Create the project and layout

  1. Create a new project using the Empty Activity template. Name the app Tab Experiment.
  2. Edit the build.gradle (Module: app) file, and add the following line to the dependencies section for the Android Design Support Library, which you need in order to use a TabLayout:
implementation 'com.android.support:design:26.1.0'

If Android Studio suggests a version with a higher number, edit the line above to update the version.

  1. In order to use a Toolbar rather than an app bar and app title, add the following attributes to the res > values > styles.xml file to hide the app bar and the title:
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
   <!-- Other style attributes -->      
   <item name="windowActionBar">false</item>
   <item name="windowNoTitle">true</item>
</style>
  1. Open activity_main.xml layout file, and click the Text tab to view the XML code.
  2. Change the ConstraintLayout to RelativeLayout, as you've done in previous exercises.
  3. Add the android:id attribute and android:padding of 16dp to the RelativeLayout.
  4. Remove the TextView supplied by the template, and add a Toolbar, a TabLayout, and a ViewPager within the RelativeLayout as shown in the code below.
<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:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    tools:context="com.example.android.tabexperiment.MainActivity">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:background="?attr/colorPrimary"
        android:minHeight="?attr/actionBarSize"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>

    <android.support.design.widget.TabLayout
        android:id="@+id/tab_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/toolbar"
        android:background="?attr/colorPrimary"
        android:minHeight="?attr/actionBarSize"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"/>

    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="fill_parent"
        android:layout_below="@id/tab_layout"/>

</RelativeLayout>

As you enter the app:popupTheme attribute for Toolbar, app will be in red if you didn't add the following statement to RelativeLayout:

<RelativeLayout xmlns:app="http://schemas.android.com/apk/res-auto"

You can click app and press Option+Enter (or Alt+Enter), and Android Studio automatically adds the statement.

2.2 Create a class and layout for each fragment

To add a fragment representing each tabbed screen, follow these steps:

  1. Click com.example.android.tabexperiment in the Android > Project pane.
  2. Choose File > New > Fragment > Fragment (Blank).
  3. Name the fragment TabFragment1.
  4. Select the Create layout XML? option.
  5. Change the Fragment Layout Name for the XML file to tab_fragment1.
  6. Clear the Include fragment factory methods? option and the Include interface callbacks? option. You don't need these methods.
  7. Click Finish.

Repeat the steps above, using TabFragment2 and TabFragment3 for Step 3, and tab_fragment2 and tab_fragment3 for Step 4.

Each fragment is created with its class definition set to extend Fragment. Also, each Fragment inflates the layout associated with the screen (tab_fragment1, tab_fragment2, and tab_fragment3), using the familiar resource-inflate design pattern you learned in a previous chapter with the options menu.

For example, TabFragment1 looks like this:

public class TabFragment1 extends Fragment {

    public TabFragment1() {
        // Required empty public constructor
    }
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment.
        return inflater.inflate(R.layout.tab_fragment1, container, false);
    }

}

2.3 Edit the fragment layout

Edit each Fragment layout XML file (tab_fragment1, tab_fragment2, and tab_fragment3):

  1. Change the FrameLayout to RelativeLayout.
  2. Change the TextView text to "These are the top stories: " and the layout_width and layout_height to wrap_content.
  3. Set the text appearance with android:textAppearance="?android:attr/textAppearanceLarge".

Repeat the steps above for each fragment layout XML file, entering different text for the TextView in step 2:

  • Text for the TextView in tab_fragment2.xml: "Tech news you can use: "
  • Text for the TextView in tab_fragment3.xml: "Cooking tips: "

Examine each fragment layout XML file. For example, tab_fragment1 should look like this:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.android.tabexperiment.TabFragment1">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="These are the top stories: "
        android:textAppearance="?android:attr/textAppearanceLarge"/>

</RelativeLayout>
  1. In the Fragment layout XML file tab_fragment1, extract the string for "These are the top stories:" into the string resource tab_1. Do the same for the strings in tab_fragment2, and tab_fragment3.

2.3 Add a PagerAdapter

The adapter-layout manager pattern lets you provide different screens of content within an Activity:

  • Use an adapter to fill the content screen to show in the Activity.
  • Use a layout manager that changes the content screens depending on which tab is selected.

Follow these steps to add a new PagerAdapter class to the app that extends FragmentStatePagerAdapter and defines the number of tabs (mNumOfTabs):

  1. Click com.example.android.tabexperiment in the Android > Project pane.
  2. Choose File > New > Java Class.
  3. Name the class PagerAdapter, and enter FragmentStatePagerAdapter into the Superclass field. This entry changes to android.support.v4.app.FragmentStatePagerAdapter.
  4. Leave the Public and None options selected, and click OK.
  5. Open PagerAdapter in the Project > Android pane. A red light bulb should appear next to the class definition. Click the bulb and choose Implement methods, and then click OK to implement the already selected getItem() and getCount() methods.
  6. Another red light bulb should appear next to the class definition. Click the bulb and choose Create constructor matching super.
  7. Add an integer member variable mNumOfTabs, and change the constructor to use it. The code should now look as follows:
public class PagerAdapter extends FragmentStatePagerAdapter {
    int mNumOfTabs;
    
    public PagerAdapter(FragmentManager fm, int NumOfTabs) {
        super(fm);
        this.mNumOfTabs = NumOfTabs;
    }

    /**
     * Return the Fragment associated with a specified position.
     *
     * @param position
     */
    @Override
    public Fragment getItem(int position) {
        return null;
    }

    /**
     * Return the number of views available.
     */
    @Override
    public int getCount() {
        return 0;
    }
}

While entering the code above, Android Studio automatically imports the following:

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;

If FragmentManager in the code is in red, a red light bulb icon should appear when you click it. Click the bulb icon and choose Import class. Import choices appear. Select FragmentManager (android.support.v4).

  1. Change the newly added getItem() method to the following, which uses a switch case block to return the Fragment to show based on which tab is clicked:
@Override
public Fragment getItem(int position) {
        switch (position) {
            case 0: return new TabFragment1();
            case 1: return new TabFragment2();
            case 2: return new TabFragment3();
            default: return null;
        }
}
  1. Change the newly added getCount() method to the following to return the number of tabs:
@Override
public int getCount() {
        return mNumOfTabs;
}

2.4 Inflate the Toolbar and TabLayout

Because you are using tabs that fit underneath the app bar, you have already set up the app bar and Toolbar in the activity_main.xml layout in the first step of this task. Now you need to inflate the Toolbar (using the same method described in a previous chapter about the options menu), and create an instance of TabLayout to position the tabs.

  1. Open MainActivity and add the following code inside the onCreate() method to inflate the Toolbar using setSupportActionBar():
protected void onCreate(Bundle savedInstanceState) {
    // ... Code inside onCreate() method
    android.support.v7.widget.Toolbar toolbar = 
                                  findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);
    // Create an instance of the tab layout from the view.
}
  1. Open strings.xml, and create the following string resources:
<string name="tab_label1">Top Stories</string>
<string name="tab_label2">Tech News</string>
<string name="tab_label3">Cooking</string>
  1. At the end of the onCreate() method, create an instance of the tab layout from the tab_layout element in the layout, and set the text for each tab using addTab():
// Create an instance of the tab layout from the view.
TabLayout tabLayout = findViewById(R.id.tab_layout);
// Set the text for each tab.
tabLayout.addTab(tabLayout.newTab().setText(R.string.tab_label1));
tabLayout.addTab(tabLayout.newTab().setText(R.string.tab_label2));
tabLayout.addTab(tabLayout.newTab().setText(R.string.tab_label3));
// Set the tabs to fill the entire layout.
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
// Use PagerAdapter to manage page views in fragments.

2.5 Use PagerAdapter to manage screen views

  1. Below the code you added to the onCreate() method in the previous task, add the following code to use PagerAdapter to manage screen (page) views in the fragments:
// Use PagerAdapter to manage page views in fragments.
// Each page is represented by its own fragment.
final ViewPager viewPager = findViewById(R.id.pager);
final PagerAdapter adapter = new PagerAdapter
              (getSupportFragmentManager(), tabLayout.getTabCount());
viewPager.setAdapter(adapter);
// Setting a listener for clicks.
  1. At the end of the onCreate() method, set a listener ( TabLayoutOnPageChangeListener) to detect if a tab is clicked, and create the onTabSelected() method to set the ViewPager to the appropriate tabbed screen. The code should look as follows:
// Setting a listener for clicks.
viewPager.addOnPageChangeListener(new
                TabLayout.TabLayoutOnPageChangeListener(tabLayout));
tabLayout.addOnTabSelectedListener(new 
                TabLayout.OnTabSelectedListener() {
    @Override
    public void onTabSelected(TabLayout.Tab tab) {
        viewPager.setCurrentItem(tab.getPosition());
    }

    @Override
    public void onTabUnselected(TabLayout.Tab tab) {
    }

    @Override
    public void onTabReselected(TabLayout.Tab tab) {
    }
});
  1. Run the app. Tap each tab to see each "page" (screen). You should also be able to swipe left and right to visit the different "pages".

Task 2 solution code

Android Studio project: TabExperiment

5. Coding challenge

Challenge: Create a new app with a navigation drawer. When the user taps a navigation drawer choice, close the drawer and display a Toast message showing which choice was selected.

A navigation drawer is a panel that usually displays navigation options on the left edge of the screen, as shown on the right side of the figure below. It is hidden most of the time, but is revealed when the user swipes a finger from the left edge of the screen or touches the navigation icon in the app bar, as shown on the left side of the figure below.

A navigation drawer

In the figure above:

  1. Navigation icon in the app bar
  2. Navigation drawer
  3. Navigation drawer menu item

To make a navigation drawer in your app, you need to create the following layouts:

  • A navigation drawer as the Activity layout root ViewGroup.
  • A navigation View for the drawer itself.
  • An app bar layout that will include a navigation icon button.
  • A content layout for the Activity that displays the navigation drawer.
  • A layout for the navigation drawer header.

After creating the layouts, you need to:

  • Populate the navigation drawer menu with item titles and icons.
  • Set up the navigation drawer and item listeners in the activity code.
  • Handle the navigation menu item selections.

To create a navigation drawer layout, use the DrawerLayout APIs available in the Support Library. For design specifications, follow the design principles for navigation drawers in the Navigation Drawer design guide.

To add a navigation drawer, use a DrawerLayout as the root view of your Activity layout. Inside the DrawerLayout, add one View that contains the main content for the screen (your primary layout when the drawer is hidden) and another View, typically a NavigationView, that contains the contents of the navigation drawer.

The figure below is a visual representation of the activity_main.xml layout and the XML layouts that it includes:

Layout for implementing a navigation drawer

In the figure above:

  1. DrawerLayout is the root view of the Activity layout.
  2. The included app_bar_main.xml uses a CoordinatorLayout as its root, and defines the app bar layout with a Toolbar which will include the navigation icon to open the drawer.
  3. The NavigationView defines the navigation drawer layout and its header, and adds menu items to it.

Challenge solution code

Android Studio project: NavDrawerExperiment

6. Summary

App bar navigation:

  • Add Up-button navigation to a child Activity by declaring the parent Activity in the AndroidManifest.xml file.
  • Declare the child's parent Activity within the child's <activity ... </activity> section:
android:parentActivityName=".MainActivity">
<meta-data android:name="android.support.PARENT_ACTIVITY"
        android:value=".MainActivity"/>

Tab navigation:

  • Tabs are a good solution for "lateral navigation" between sibling views.
  • The primary class used for tabs is TabLayout in the Android Design Support Library.
  • ViewPager is a layout manager that allows the user to flip left and right through pages of data. ViewPager is most often used in conjunction with Fragment.
  • Use one of the two standard adapters for using ViewPager: FragmentPagerAdapter or FragmentStatePagerAdapter.

7. Related concept

The related concept documentation is in 4.4: User navigation.

8. Learn more

Android developer documentation:

Material Design spec:

Android Developers Blog: Android Design Support Library

Other:

9. Homework

This section lists possible homework assignments for students who are working through this codelab as part of a course led by an instructor. It's up to the instructor to do the following:

  • Assign homework if required.
  • Communicate to students how to submit homework assignments.
  • Grade the homework assignments.

Instructors can use these suggestions as little or as much as they want, and should feel free to assign any other homework they feel is appropriate.

If you're working through this codelab on your own, feel free to use these homework assignments to test your knowledge.

Build and run an app

Create an app with a main Activity and at least three other Activity children. Each Activity should have an options menu and use the v7 appcompat support library Toolbar as the app bar, as shown below.

  1. In the main Activity, build a grid layout with images of your own choosing. Three images (donut_circle.png, froyo_circle.png, and icecream_circle.png), which you can download, are provided as part of the DroidCafe app.
  2. Resize the images if necessary, so that three of them fit horizontally on the screen in the grid layout.
  3. Enable each image to provide navigation to a child Activity. When the user taps the image, it starts a child Activity. From each child Activity, the user should be able to tap the Up button in the app bar (highlighted in the figure below) to return to the main Activity.

Up button for up navigation

Answer these questions

Question 1

Which template provides an Activity with an options menu and the v7 appcompat support library Toolbar as the app bar? Choose one:

  • Empty Activity template
  • Basic Activity template
  • Navigation Drawer Activity template
  • Bottom Navigation Activity

Question 2

Which dependency do you need in order to use a TabLayout? Choose one:

  • com.android.support:design
  • com.android.support.constraint:constraint-layout
  • junit:junit:4.12
  • com.android.support.test:runner

Question 3

Where do you define each child Activity and parent Activity to provide Up navigation? Choose one:

  • To provide the Up button for a child screen Activity, declare the parent Activity in the child Activity section of the activity_main.xml file.
  • To provide the Up button for a child screen Activity, declare the parent Activity in the "main" XML layout file for the child screen Activity.
  • To provide the Up button for a child screen Activity, declare the parent Activity in the child Activity section of the AndroidManifest.xml file.
  • To provide the Up button for a child screen Activity, declare the parent Activity in the parent Activity section of the AndroidManifest.xml file.

Submit your app for grading

Guidance for graders

Check that the app has the following features:

  • A GridLayout in the content_main.xml file.
  • A new Intent and startActivity() method for each navigation element in the grid.
  • A separate Activity for each navigation element in the grid.

10. Next codelab

To find the next practical codelab in the Android Developer Fundamentals (V2) course, see Codelabs for Android Developer Fundamentals (V2).

For an overview of the course, including links to the concept chapters, apps, and slides, see Android Developer Fundamentals (Version 2).