4.4: User navigation
Contents:
- Providing users with a path through your app
- Back-button navigation
- Hierarchical navigation patterns
- Ancestral navigation (the Up button)
- Descendant navigation
- Lateral navigation with tabs and swipes
- Related practical
- Learn more
Providing users with a path through your app
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.
Often you need several paths through your app that offer the following types of navigation:
- Back navigation, where users navigate to the previous screen using the Back button.
- Hierarchical navigation, where users navigate through a hierarchy of screens. The hierarchy is organized with a parent screen for every set of child screens.
Back-button navigation
Back-button navigation—navigation back through the history of screens—is deeply rooted in the Android system. Android users expect the Back button in the bottom left corner of every screen to take them to the previous screen. The set of historical screens always starts with the user's Launcher (the device's Home screen), as shown in the figure below. Pressing Back enough times should return the user back to the Launcher.
In the figure above:
- Starting from Launcher.
- Clicking the Back button to navigate to the previous screen.
You don't have to manage the Back button in your app. The system handles tasks and the back stack—the list of previous screens—automatically. The Back button by default simply traverses this list of screens, removing the current screen from the list as the user presses it.
There are, however, cases where you may want to override the behavior for the Back button. For example, if your screen contains an embedded web browser in which users can interact with page elements to navigate between web pages, you may wish to trigger the embedded browser's default back behavior when users press the device's Back button.
The onBackPressed()
method of the Activity
class is called whenever the Activity
detects the user's press of the Back key. The default implementation simply finishes the current Activity
, but you can override this to do something else:
@Override
public void onBackPressed() {
// Add the Back key handler here.
return;
}
If your code triggers an embedded browser with its own behavior for the Back key, you should return the Back key behavior to the system's default behavior if the user uses the Back key to go beyond the beginning of the browser's internal history.
Hierarchical navigation patterns
To give the user a path through the full range of an app's screens, the best practice is to use some form of hierarchical navigation. An app's screens are typically organized in a parent-child hierarchy, as shown in the figure below:
In the figure above:
- Parent screen
- First-level child screen siblings
- Second-level child screen siblings
Parent screen
A parent screen (such as a news app's home screen) enables navigation down to child screens.
- The main
Activity
of an app is usually the parent screen. - Implement a parent screen as an activity with descendant navigation to one or more child screens.
First-level child screen siblings
Siblings are screens in the same position in the hierarchy that share the same parent screen (like brothers and sisters).
- In the first level of siblings, the child screens may be collection screens that collect the headlines of stories, as shown above.
- Implement each child screen as an
Activity
orFragment
. - Implement lateral navigation to navigate from one sibling to another on the same level.
- If there is a second level of screens, the first level child screen is the parent to the second level child screen siblings. Implement descendant navigation to the second-level child screens.
Second-level child screen siblings
In news apps and others that offer multiple levels of information, the second level of child screen siblings might offer content, such as stories.
- Implement each second-level child screen sibling as another
Activity
orFragment
. - Stories at this level may include embedded story elements such as videos, maps, and comments, which might be implemented as fragments.
You can enable the user to navigate up to and down from a parent, and sideways among siblings:
- Descendant navigation: Navigating down from a parent screen to a child screen.
- Ancestral navigation: Navigating up from a child screen to a parent screen.
- Lateral navigation: Navigating from one sibling to another sibling (at the same level).
You can use the main Activity
of the app as a parent screen, and then add an Activity
or Fragment
for each child screen.
Main Activity with an activity for each child
If the first-level child screen siblings have another level of child screens under them, you should implement each first-level screen as an activity, so that the lifecycle of each screen is managed properly before calling any second-level child screens.
For example, in the figure above, the parent screen is most likely the main activity. An app's main activity (usually MainActivity.java
) is typically the parent screen for all other screens in your app. You implement a navigation pattern in the main activity to enable the user to go to another activity or fragment. For example, you can implement navigation using an Intent
that starts an Activity
.
Tip: Using an Intent
in the current activity to start another activity adds the current activity to the call stack, so that the Back button in the other activity (described in the previous section) returns the user to the current activity.
As you've learned, the Android system initiates code in an Activity
with callback methods that manage the Act
activityivity
lifecycle for you. (A previous lesson covers the activity lifecycle; for more information, see Activities in the Android developer documentation.)
The declaration of each child activity is defined in the AndroidManifest.xml
file with its parent activity. For example, the following defines OrderActivity
as a child of the parent MainActivity
:
<activity android:name=".OrderActivity"
android:label="@string/title_activity_order"
android:parentActivityName=
"com.example.android.droidcafe.MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".MainActivity"/>
</activity>
Main Activity with a Fragment for each child
If the child screen siblings do not have another level of child screens under them, you can define each one as a Fragment
, which represents a behavior or portion of a UI within in an activity. Think of a fragment as a modular section of an activity which has its own lifecycle, receives its own input events, and which you can add or remove while the activity is running.
You can add more than one fragment in a single activity. For example, in a section sibling screen showing a news story and implemented as an Activity
, you might have a child screen for a video clip implemented as a Fragment
. You would implement a way for the user to navigate to the video clip Fragment
, and then back to the Activity
that shows the story.
Ancestral navigation (the Up button)
With ancestral navigation in a multitier hierarchy, you enable the user to go up from a section sibling to the collection sibling, and then up to the parent screen.
In the figure above:
- Up button for ancestral navigation from the first-level siblings to the parent.
- Up button for ancestral navigation from second-level siblings to the first-level child screen acting as a parent screen.
The Up button is used to navigate within an app based on the hierarchical relationships between screens. For example (referring to the figure above):
- If a first-level child screen offers headlines to navigate to second-level child screens, the second-level child screen siblings should offer Up buttons that return to the first-level child screen, which is their shared parent.
- If the parent screen offers navigation to first-level child siblings, then the first-level child siblings should offer an Up button that returns to the parent screen.
- If the parent screen is the topmost screen in an app (that is, the app's home screen), it should not offer an Up button.
Tip: The Back button below the screen differs from the Up button. The Back button provides navigation to whatever screen you viewed previously. If you have several children screens that the user can navigate through using a lateral navigation pattern (as described later in this chapter), the Back button would send the user back to the previous child screen, not to the parent screen. Use an Up button if you want to provide ancestral navigation from a child screen back to the parent screen. For more information about Up navigation, see Providing Up Navigation. See the concept chapter on menus and pickers for details on how to implement the app bar.
To provide the Up button for a child screen Activity
, declare the parent of the Activity
to be the main Activity
in the AndroidManifest.xml
file:
<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>
The snippet above in AndroidManifest.xml
declares the parent for the child screen OrderActivity
to be MainActivity
. It also sets the android:label
to a title for the Activity
screen to be "Order Activity". The child screen now includes the Up button in the app bar (highlighted in the figure below), which the user can tap to navigate back to the parent screen.
Descendant navigation
With descendant navigation, you enable the user to go from the parent screen to a first-level child screen, and from a first-level child screen down to a second-level child screen.
In the figure above:
- Descendant navigation from parent to first-level child screen
- Descendant navigation from headline in a first-level child screen to a second-level child screen
Buttons or targets
The best practice for descendant navigation from the parent screen to collection siblings is to use buttons or simple targets such as an arrangement of images or iconic buttons (also known as a dashboard). When the user touches a button, the collection sibling screen opens, replacing the current context (screen) entirely.
Tip: Buttons and simple targets are rarely used for navigating to section siblings within a collection. See lists, carousels, and cards in the next section.
In the figure above:
- Buttons on a parent screen
- Targets (Image buttons or icons) on a parent screen
- Descendant navigation pattern from parent screen to first-level child siblings
A dashboard usually has either two or three rows and columns, with large touch targets to make it easy to use. Dashboards are best when each collection sibling is equally important. You can use a LinearLayout
, RelativeLayout
, or GridLayout
. See Layouts for an overview of how layouts work.
Navigation drawer
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.
In the figure above:
- Navigation icon in the app bar
- Navigation drawer
- Navigation drawer menu item
A good example of a navigation drawer is in the Gmail app, which provides access to the inbox, labeled email folders, and settings. The best practice for employing a navigation drawer is to provide descendant navigation from the parent Activity
to all of the other child screens in an app. It can display many navigation targets at once—for example, it can contain buttons (like a dashboard), tabs, or a list of items (like the Gmail drawer).
To make a navigation drawer in your app, you need to create the following layouts:
- A navigation drawer as the
Activity
layout rootViewGroup
- A navigation
View
for the drawer itself - An app bar layout that includes room for a navigation icon button
- A content layout for the
Activity
that displays the navigation drawer - A layout for the navigation drawer header
Follow these general steps:
- 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.
Creating the navigation drawer layout
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 ViewGroup
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.
Tip: To make your layouts simpler to understand, use the include
tag to include an XML layout within another XML layout.
For example, the following layout uses:
- A
DrawerLayout
as the root of theActivity
layout inactivity_main.xml
. - The main content of screen defined in the
app_bar_main.xml
layout file. - A
NavigationView
that represents a standard navigation menu that can be populated by a menu resource XML file.
Refer to the figure below that corresponds to this layout:
<android.support.v4.widget.DrawerLayout
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/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start">
<include
layout="@layout/app_bar_main"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<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/nav_header_main"
app:menu="@menu/activity_main_drawer" />
</android.support.v4.widget.DrawerLayout>
In the figure above:
DrawerLayout
is the rootViewGroup
of theActivity
layout.- The included
app_bar_main.xml
uses aCoordinatorLayout
as its root, and defines the app bar layout with aToolbar
which will include the navigation icon to open the drawer. - The
NavigationView
defines the navigation drawer layout and its header, and adds menu items to it.
Note the following in the activity_main.xml
layout:
- The
android:id
for theDrawerLayout
isdrawer_layout
. You will use thisid
to instantiate adrawer
object in your code. - The
android:id
for theNavigationView
isnav_view
. You will use thisid
to instantiate anavigationView
object in your code. - The
NavigationView
must specify its horizontal gravity with theandroid:layout_gravity
attribute. Use the"start"
value for this attribute (rather than"left"
), so that if the app is used with right-to-left (RTF) languages, the drawer appears on the right rather than the left side.
android:layout_gravity="start"
- Use the
android:fitsSystemWindows="true"
attribute to set the padding of theDrawerLayout
and theNavigationView
to ensure the contents don't overlay the system windows.DrawerLayout
usesfitsSystemWindows
as a sign that it needs to inset its children (such as the main contentViewGroup
), but still draw the top status bar background in that space. As a result, the navigation drawer appears to be overlapping, but not obscuring, the translucent top status bar. The insets you get fromfitsSystemWindows
will be correct on all platform versions to ensure that your content does not overlap with system-provided UI components.
The navigation drawer header
The NavigationView
specifies the layout for the header of the navigation drawer with the attribute app:headerLayout="@layout/nav_header_main"
. The nav_header_main.xml
file defines the layout of this header to include an ImageView
and a TextView
, which is typical for a navigation drawer, but you could also include other View
elements.
Tip: The header's height should be 160dp, which you should extract into a dimension resource (nav_header_height
).
The following is the code for the nav_header_main.xml
file:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/nav_header_height"
android:background="@drawable/side_nav_bar"
android:gravity="bottom"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:theme="@style/ThemeOverlay.AppCompat.Dark">
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="@dimen/nav_header_vertical_spacing"
android:src="@android:drawable/sym_def_app_icon" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/nav_header_vertical_spacing"
android:text="@string/my_app_title"
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
</LinearLayout>
The app bar layout
The include
tag in the activity_main.xml
layout file includes the app_bar_main.xml
layout file, which uses a CoordinatorLayout
as its root. The app_bar_main.xml
file defines the app bar layout with the Toolbar
class as shown previously in the chapter about menus and pickers. It also defines a floating action button, and uses an include
tag to include the content_main.xml
layout.
The following is the code for the app_bar_main.xml
file:
<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:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="com.example.android.navigationexperiments.MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
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:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<include layout="@layout/content_main" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
android:src="@android:drawable/ic_dialog_email" />
</android.support.design.widget.CoordinatorLayout>
Note the following:
- The
app_bar_main.xml
layout uses aCoordinatorLayout
as its root, and includes thecontent_main.xml
layout. - The
app_bar_main.xml
layout uses theandroid:fitsSystemWindows="true"
attribute to set the padding of the app bar to ensure that it doesn't overlay the system windows such as the status bar.
The content layout for the main activity screen
The layout above uses an include
tag to include the content_main.xml
layout, which defines the layout of the main Activity
screen. In the example layout below, the main Activity
screen shows a TextView
that displays the string "Hello World!". The following is the code for the content_main.xml
file:
<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"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="com.example.android.navigationexperiments.MainActivity"
tools:showIn="@layout/app_bar_main">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />
</RelativeLayout>
Note the following:
- The
content_main.xml
layout must be the first child in theDrawerLayout
because the drawer must be on top of the content. In our layout above, thecontent_main.xm
layout is included in theapp_bar_main.xml
layout, which is the first child. - The
content_main.xml
layout uses aRelativeLayout
ViewGroup
set to match the parent view's width and height, because it represents the entire UI when the navigation drawer is hidden. - The layout behavior for the
RelativeLayout
is set to the string resource@string/appbar_scrolling_view_behavior
, which controls the scrolling behavior of the screen in relation to the app bar at the top. TheAppBarLayout.ScrollingViewBehavior
class defines this behavior. View elements that scroll vertically should use this behavior, because it supports nested scrolling to automatically scroll anyAppBarLayout
siblings.
Populating the navigation drawer menu
The NavigationView
in the activity_main.xml
layout specifies the menu items for the navigation drawer using the following statement:
app:menu="@menu/activity_main_drawer"
The menu items are defined in the activity_main_drawer.xml
file, which is located under app > res > menu in the Project > Android pane. The <group></group>
tag defines a menu group—a collection of items that share traits, such as whether they are visible, enabled, or checkable. A group must contain one or more <item></>
elements and be a child of a <menu>
element, as shown below. In addition to defining each menu item's title with the android:title
attribute, the file also defines each menu item's icon with the android:icon
attribute.
The group is defined with the android:checkableBehavior
attribute. This attribute lets you put interactive elements within the navigation drawer, such as toggle switches that can be turned on or off, and checkboxes and radio buttons that can be selected. The choices for this attribute are:
single
: Only one item from the group can be selected. Use for radio buttons.all
: All items can be selected. Use for checkboxes.none
: No items can be selected.
The following XML code snippet shows how to define a menu group:
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="none">
<item
android:id="@+id/nav_camera"
android:icon="@drawable/ic_menu_camera"
android:title="@string/import_camera" />
<item
android:id="@+id/nav_gallery"
android:icon="@drawable/ic_menu_gallery"
android:title="@string/gallery" />
<item
android:id="@+id/nav_slideshow"
android:icon="@drawable/ic_menu_slideshow"
android:title="@string/slideshow" />
<item
android:id="@+id/nav_manage"
android:icon="@drawable/ic_menu_manage"
android:title="@string/tools" />
</group>
<item android:title="@string/communicate">
<menu>
<item
android:id="@+id/nav_share"
android:icon="@drawable/ic_menu_share"
android:title="@string/share" />
<item
android:id="@+id/nav_send"
android:icon="@drawable/ic_menu_send"
android:title="@string/send" />
</menu>
</item>
</menu>
Setting up the navigation drawer and item listeners
To use a listener for the navigation drawer's menu items, the Activity
hosting the navigation drawer must implement the OnNavigationItemSelectedListener
interface:
Implement
NavigationView.OnNavigationItemSelectedListener
in the class definition:public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
This interface offers the
onNavigationItemSelected()
method, which is called when an item in the navigation drawer menu item is tapped. As you enterOnNavigationItemSelectedListener
, the red light bulb appears on the left margin.Click the light bulb, choose Implement methods, and choose the onNavigationItemSelected(item:MenuItem):boolean method.
Android Studio adds a stub for the method:
@Override public boolean onNavigationItemSelected(MenuItem item) { return false; }
You learn how to use this stub in the next section.
Before setting up the navigation item listener, add code to the
onCreate()
method for theActivity
to instantiate theDrawerLayout
andNavigationView
objects (drawer
andnavigationView
in the code below):@Override protected void onCreate(Bundle savedInstanceState) { // ... Rest of onCreate code. DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close); if (drawer != null) { drawer.addDrawerListener(toggle); } toggle.syncState(); NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view); if (navigationView != null) { navigationView.setNavigationItemSelectedListener(this); } }
The code above instantiates an
ActionBarDrawerToggle
, which substitutes a special drawable for the Up button in the app bar, and links theActivity
to theDrawerLayout
. The special drawable appears as a "hamburger" navigation icon when the drawer is closed, and animates into an arrow as the drawer opens.
ActionBarDrawerToggle
in support-library-v7.appcompact
, not the version in support-library-v4
.
Tip: You can customize the animated toggle by defining the drawerArrowStyle
in your ActionBar
theme. For more detailed information about the ActionBar
theme, see Adding the App Bar in the Android Developer documentation.
The code above implements addDrawerListener()
to listen for drawer open and close events, so that when the user taps custom drawable button, the navigation drawer slides out.
You must also use the syncState()
method of ActionBarDrawerToggle
to synchronize the state of the drawer indicator. The synchronization must occur after the DrawerLayout
instance state has been restored, and any other time when the state may have diverged in such a way that the ActionBarDrawerToggle
was not notified.
The code above ends by setting a listener, setNavigationItemSelectedListener()
, to the navigation drawer to listen for item clicks.
The ActionBarDrawerToggle
also lets you specify the strings to use to describe the open/close drawer actions for accessibility services. Define the strings in your strings.xml
file:
<string name="navigation_drawer_open">Open navigation drawer</string>
<string name="navigation_drawer_close">Close navigation drawer</string>
Handling navigation menu item selections
Add code to the onNavigationItemSelected()
method stub to handle menu item selections. This method is called when an item in the navigation drawer menu is tapped. You can use switch case
statements to take the appropriate action based on the menu item's id
, which you can retrieve using the getItemId()
method:
@Override
public boolean onNavigationItemSelected(MenuItem item) {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
// Handle navigation view item clicks here.
switch (item.getItemId()) {
case R.id.nav_camera:
// Handle the camera import action (for now display a toast).
drawer.closeDrawer(GravityCompat.START);
displayToast(getString(R.string.chose_camera));
return true;
case R.id.nav_gallery:
// Handle the gallery action (for now display a toast).
drawer.closeDrawer(GravityCompat.START);
displayToast(getString(R.string.chose_gallery));
return true;
case R.id.nav_slideshow:
// Handle the slideshow action (for now display a toast).
drawer.closeDrawer(GravityCompat.START);
displayToast(getString(R.string.chose_slideshow));
return true;
case R.id.nav_manage:
// Handle the tools action (for now display a toast).
drawer.closeDrawer(GravityCompat.START);
displayToast(getString(R.string.chose_tools));
return true;
case R.id.nav_share:
// Handle the share action (for now display a toast).
drawer.closeDrawer(GravityCompat.START);
displayToast(getString(R.string.chose_share));
return true;
case R.id.nav_send:
// Handle the send action (for now display a toast).
drawer.closeDrawer(GravityCompat.START);
displayToast(getString(R.string.chose_send));
return true;
default:
return false;
}
}
After the user taps a navigation drawer selection or taps outside the drawer, the DrawerLayout
closeDrawer()
method closes the drawer.
Lists and carousels
Use a scrolling list, such as a RecyclerView
, to provide navigation targets for descendant navigation. Vertically scrolling lists are often used for a screen that lists stories, with each list item acting as a button to each story. For more visual or media-rich content items such as photos or videos, you may want to use a horizontally scrolling list (also known as a carousel). These UI elements are good for presenting items in a collection (for example, a list of news stories).
You learn about RecyclerView
in another chapter.
Master/detail navigation flow
In a master/detail navigation flow, a master screen contains a list of items, and a detail screen shows detailed information about one item. You usually implement descendant navigation using one of the following techniques:
- Use an intent to starts an activity that represents the detail screen. For more information about intents, see Intents and Intent Filters in the Android developer documentation.
- When adding a Settings
Activity
, extendPreferenceActivity
to create a two-pane master/detail layout to support large screens. Replace the activity content with a SettingsFragment
. This is a useful pattern if you have multiple groups of settings and need to support tablet-sized screens as well as smartphones. You learn about the Settings activity andPreferenceActivity
in another chapter. For more information about using fragments, see Fragments in the Android developer documentation.
Smartphones are best suited for displaying one screen at a time—for example a master screen (on the left side of the figure below) and a detail screen (on the right side of the figure below).
On the other hand, tablet displays, especially when viewed in the landscape orientation, are best suited for showing multiple content panes at a time: the master on the left, and the detail to the right, as shown below.
Options menu in the app bar
The app bar typically contains the options menu, which is most often used for navigation patterns for descendant navigation. It may also contain an Up button for ancestral navigation, a nav icon for opening a navigation drawer, and a filter icon to filter page views. You learn how to set up the options menu and the app bar in another chapter.
Lateral navigation with tabs and swipes
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, or from one top story to the next, without having to navigate back up to the parent screen.
In the figure above:
- Lateral navigation from one category screen to another
- Lateral navigation from one story screen to another
Another example of lateral navigation is the ability to swipe left or right in a Gmail conversation to view a newer or older email in the same inbox.
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, providing navigation to other screens. Tab navigation is a common 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.
Tabs are most appropriate for small sets (four or fewer) of sibling screens. You can combine tabs with swipe views, so that the user can swipe across from one screen to another as well as tap a tab.
Tabs offer two benefits:
- Because there is a single, initially selected tab, users already have access to that tab's content from the parent screen without any further navigation.
- Users can navigate quickly between related screens, without needing to first revisit the parent.
Keep in mind the following best practices when using tabs:
- Tabs are usually laid out horizontally.
- Tabs should always run along the top of the screen, and should not be aligned to the bottom of the screen.
- Tabs should be persistent across related screens. Only the designated content region should change when tapping a tab, and tab indicators should remain available at all times.
- Switching to another tab should not be treated as history. For example, if a user switches from tab A to tab B, pressing the Up button in the app bar should not reselect tab A but should instead return the user to the parent screen.
The key steps for implementing tabs are as follows:
- Define the tab layout. The main class used for displaying tabs is
TabLayout
. It provides a horizontal layout to display tabs. You can show the tabs below the app bar. - Implement a
Fragment
for each tab content screen. AFragment
is a behavior or a portion of a UI within anActivity
. It's like a mini-Activity
within the mainActivity
, with its own lifecycle. One benefit of using aFragment
for each tabbed content is that you can isolate the code for managing the tabbed content inside theFragment
. To learn aboutFragment
, see Fragments in the API Guide. - Add a pager adapter. Use the
PagerAdapter
class to populate "pages" (screens) inside of aViewPager
, which is a layout manager that lets the user flip left and right through screens of data. You supply an implementation of aPagerAdapter
to generate the screens that theView
shows.ViewPager
is most often used in conjunction withFragment
, which is a convenient way to supply and manage the lifecycle of each screen. - Create an instance of the tab layout, and set the text for each tab.
- Use
PagerAdapter
to manage screens ("pages"). Each screen is represented by its ownFragment
. - Set a listener to determine which tab is tapped.
There are standard adapters for using a Fragment
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 eachFragment
as the user navigates to another screen, minimizing memory usage.
Defining tab layout
To use a TabLayout
, you can design the main Activity
layout to use a Toolbar
for the app bar, a TabLayout
for the tabs below the app bar, and a ViewPager
within the root layout to switch child elements. The layout should look similar to the following, assuming each child element fills the screen:
<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"/>
For each child view, create a layout for each Fragment
such as tab_fragment1.xml
, tab_fragment2.xml
, and so on.
Implementing each fragment
A Fragment
is a behavior or a portion of a UI within an Activity
. It's like a mini-Activity
within the main Activity
, with its own lifecycle. To learn about Fragment
, see Fragments in the API Guide.
Add a class for each Fragment
(such as TabFragment1.java
, TabFragment2.java
, and TabFragment3.java
) representing each screen the user can visit by clicking a tab. Each class should extend Fragment
and inflate the layout associated with the screen (tab_fragment1.xml
, tab_fragment2.xml
, and tab_fragment3.xml
). For example, TabFragment1.java
looks like this:
public class TabFragment1 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.tab_fragment1, container, false);
}
}
Adding a pager adapter
Add a PagerAdapter
that extends FragmentStatePagerAdapter
. The code should do the following:
- Define the number of tabs.
- Use the
getItem()
method of theAdapter
class to determine which tab is clicked. - Use a
switch case
block to return the screen (page) to show based on which tab is clicked.
The following is an example:
public class PagerAdapter extends FragmentStatePagerAdapter {
int mNumOfTabs;
public PagerAdapter(FragmentManager fm, int NumOfTabs) {
super(fm);
this.mNumOfTabs = NumOfTabs;
}
@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;
}
}
@Override
public int getCount() {
return mNumOfTabs;
}
}
Creating an instance of the tab layout
In the onCreate()
method of the main Activity
, create an instance of the tab layout from the tab_layout
element in the layout, and set the text for each tab using addTab()
:
@Override
protected void onCreate(Bundle savedInstanceState) {
// ... Rest of onCreate code
// Create an instance of the tab layout from the view.
TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
// Set the text for each tab.
tabLayout.addTab(tabLayout.newTab().setText("Top Stories"));
tabLayout.addTab(tabLayout.newTab().setText("Tech News"));
tabLayout.addTab(tabLayout.newTab().setText("Cooking"));
// Set the tabs to fill the entire layout.
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
// Use PagerAdapter to manage page views in fragments.
}
Managing screen views in fragments with a listener
Use PagerAdapter
in the onCreate()
method of the main Activity
to manage screen ("page") views in each Fragment
. Each screen is represented by its own Fragment
. You also need to set a listener to determine which tab is tapped. The following code should appear after the code from the previous section in the onCreate()
method:
@Override
protected void onCreate(Bundle savedInstanceState) {
// ... Rest of onCreate code
// Use PagerAdapter to manage page views in fragments.
final ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
final PagerAdapter adapter = new PagerAdapter
(getSupportFragmentManager(), tabLayout.getTabCount());
viewPager.setAdapter(adapter);
// 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) {
}
});
}
Using ViewPager for swipe views (horizontal paging)
ViewPager
is a layout manager that lets the user flip left and right through "pages" (screens) of content. ViewPager
is most often used in conjunction with Fragment
, which is a convenient way to supply and manage the lifecycle of each "page". ViewPager
also provides the ability to swipe "pages" horizontally.
In the previous example, you used a ViewPager
within the root layout to switch child screens. This provides the ability for the user to swipe from one child screen to another. Users are able to navigate to sibling screens by touching and dragging the screen horizontally in the direction of the desired adjacent screen.
Swipe views are most appropriate where there is some similarity in content type among sibling pages, and when the number of siblings is relatively small. In these cases, this pattern can be used along with tabs above the content region to indicate the current page and available pages, to aid discoverability and provide more context to the user.
Tip: It's best to avoid horizontal paging when child screens contain horizontal panning surfaces (such as maps), as these conflicting interactions may deter your screen's usability.
Related practical
The related practical is 4.4: User navigation.
Learn more
Android developer documentation:
- User Interface & Navigation
- Designing effective navigation
- Implementing effective navigation
- Creating swipe views with tabs
- Create a navigation drawer
- Designing Back and Up navigation
- Providing Up navigation
- Implementing Descendant Navigation
TabLayout
- Navigation Drawer
DrawerLayout
- Support Library
Material Design spec:
Android Developers Blog: Android Design Support Library
Other:
- AndroidHive: Android Material Design working with Tabs
- Truiton: Android Tabs Example – With Fragments and ViewPager