Many applications involve several screens of data that a user might want to browse or flip through to view each screen. As an example, think of an application where we list a catalogue of books with each book in the catalogue appearing on a single screen. A book's screen contains an image, title, and description like the following screenshot:

To view each book's information, the user needs to move to each screen. We could put a next button and a previous button on the screen, but a more natural action is for the user to use their thumb or finger to swipe the screen from one edge of the display to the other and have the screen with the next book's information slide into place as represented in the following screenshot:

This creates a very natural navigation experience, and honestly, is a more fun way to navigate through an application than using buttons.
Implementing swipe navigation is pretty simple, and fragments are at the core. Each of the screens is implemented as a fragment derived class. Each screen can be a completely different fragment derived class or the screens can be instances of the same fragment derived class with different data. To create a book browser app such as the one shown in the previous screenshot, we can use a simple fragment derived class that sets the book image, title, and description.
There is one thing about the fragment derived class that is a bit unusual. At the time of this writing, the classes involved in managing swipe navigation are relatively new and are only available in the android.support.v4.app package. As a result, the fragment derived class created by us must inherit from the support package version of the Fragment class, android.support.v4.app.Fragment, even when our app is targeting the Android versions that natively support fragments. The fragment class definition will appear similar to the following code:
import android.support.v4.app.Fragment;
public class BookFragment extends Fragment {
// members elided for clarity
}Presenting the individual fragments that represent the application screens requires an adapter to manage the creation and delivery of each fragment. The Android support library includes two classes that provide this capability: FragmentPagerAdapter and FragmentStatePagerAdapter.
The FragmentPagerAdapter class is useful for scenarios where there are a small number of fragments. When a given fragment instance is created, it is directly stored in the FragmentManager class and that same instance is re-used each time that fragment's page is displayed. The fragment's onDestroyView method is called when the user switches to a different fragment, but not the onDestroy method. It's important that we only use the FragmentPagerAdapter class in cases where there's a relatively small number of fragments, because we should assume that once a fragment is created, it will exist as long as the FragmentPagerAdapter class exists.
The FragmentStatePagerAdapter class is useful for scenarios where there is a large number of fragments, because fragments may be destroyed when they are no longer visible. Fragments managed by FragmentStatePagerAdapter will always have their onDestroyView method called, and may have their onDestroy method called as well. The call to the onDestroy method does not necessarily occur as soon as the user swipes to another fragment it may occur much later depending on the device's available resources. The FragmentStatePagerAdapter class gives the fragment an opportunity to save its state through the platform's call to the onSaveInstanceState method.
The ability to discard and recreate the contained fragments also makes the FragmentStatePagerAdapter class useful for scenarios where the list of fragments being displayed may change. The details of implementing an updatable FragmentStatePagerAdapter instance are beyond the scope of this book, but an example is available at http://bit.ly/UpdateFragmentStatePagerAdapter.
To create a book browser app as seen in the previous screenshot, we'll extend the FragmentPagerAdapter class because we'll be displaying just a few books. We'll name our class as BookPagerAdapter, the declaration of which is shown in the following code:
public class BookPagerAdapter extends FragmentPagerAdapter {
// members elided for clarity
}To implement our BookPagerAdapter class, we just need to override a few methods. The primary method, getItem, is responsible for returning each fragment instance. Our getItem method appears as follows:
public Fragment getItem(int idx) {
// Store the argument values for this fragment
Bundle arguments = new Bundle();
arguments.putString(
BookFragment.BOOK_TITLE, mCourseTitles[idx]);
arguments.putString(
BookFragment.BOOK_DESCRIPTIONS, mCourseDescriptions[idx]);
arguments.putInt(
BookFragment.TOP_IMAGE, mTopImageResourceIds[idx]);
// Create the fragment instance and pass the arguments
BookFragment bookFragment = new BookFragment();
bookFragment.setArguments(arguments);
// return the fragment instance
return bookFragment;
}When the app displays a particular book's page for the first time, the getItem method is called with the index of the page as a parameter named idx in our code. Before creating the fragment, we retrieve the book title, description, and image resource ID from arrays containing those values and store them in a Bundle instance. We then create an instance of our BookFragment class and associate the argument Bundle instance with it. Finally, we return the BookFragment reference. When our BookFragment instance is displayed, it will access the values in the argument Bundle instance and display them.
We now must override two additional methods: getPageTitle and getCount. The getPageTitle method returns the string that is visible in the thin bar above each fragment. Like the getItem method, the getPageTitle method receives the index of the page being displayed. The getPageTitle method simply returns a value from an array containing short versions of the page title, as shown in the following code:
public CharSequence getPageTitle(int idx) {
return mCourseTitlesShort[idx];
}The getCount method is responsible for returning the number of screens we'll be displaying. We can simply return the length of the array we're using in the getPageTitle method, as shown in the following code:
public int getCount() {
return mCourseTitlesShort.length;
}Implementing our BookPagerAdapter class takes care of the code that manages our fragments. Now, we just need to put the appropriate layout within our activity and connect it with the adapter.
The swipe user interface behavior and effects come from two Android classes: ViewPager and PagerTitleStrip. The ViewPager class is the primary class. It manages the user interaction, provides the swipe animation effects, and cooperates with the adapter class that provides each screen's fragment. The PagerTitleStrip class handles the displaying of the thin title bar above each fragment. The string values returned from our BookPagerAdapter class' getPageTitle method are displayed within the PagerTitleStrip instance.
We'll create a layout resource file for our application's activity called activity_main.xml containing the ViewPager and PagerTitleStrip classes, as shown in the following XML layout:
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.view.PagerTitleStrip
android:id="@+id/pager_title_strip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:background="#33b5e5"
android:paddingBottom="4dp"
android:paddingTop="4dp"
android:textColor="#fff"/>
</android.support.v4.view.ViewPager>Our layout resource file contains ViewPager as the root node, and is set to occupy the entire activity. The ViewPager class has an ID value of pager. The PagerTitleStrip class is set to fill the full width of ViewPager, and to be positioned at the top. Alternatively we could set the layout_gravity attribute to a value of bottom to position PagerTitleStrip at the bottom of the ViewPager class' display area. Although, other values for the layout_gravity attribute are technically valid, they tend to be problematic. As a general rule, we want to limit our choices for the layout_gravity attribute to be either top or bottom.
We have our complete layout and have already created the adapter that will manage the fragments within our application. We're now ready to declare our activity class, which we'll name MainActivity. The class declaration appears as follows:
import android.support.v4.app.FragmentActivity;
import android.support.v4.view.ViewPager;
public class MainActivity extends FragmentActivity {
BookPagerAdapter mBookPagerAdapter;
ViewPager mViewPager;
// other members elided for clarity
}Notice that we're inheriting from the support library class FragmentActvity rather than the regular Activity class. This is due to the same issue we discussed when we declared our BookFragment class. The classes that provide the swipe behavior are in the support library; therefore, they expect all fragment-related classes to be from that library. Our activity class includes member variables for our BookPagerAdapter
and ViewPager classes.
The last thing we need to do is connect our BookPagerAdapter class to the ViewPager class. We'll do that in our onCreate method, which appears as follows:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBookPagerAdapter = new BookPagerAdapter(
getSupportFragmentManager(), this);
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mBookPagerAdapter);
}As we can see, our job here is pretty easy. We call the setContentView method with the R.layout.activity_main resource we just created. When the setContentView method returns, we create our BookPagerAdapter instance passing the activity's FragmentManager instance and the activity's this pointer so our BookPagerAdapter can use it as the context. With our BookPagerAdapter created, we use the activity class' findViewById method to get a reference to the ViewPager class that we created with the layout resource file. Finally, we call the ViewPager instance's setAdapter method to connect the BookPagerAdapter instance to our ViewPager instance.
We now have everything in place. Our book browser is all ready for the user to browse through our list of books using swipe navigation.
If we're working with Android Studio, getting started at building an app with swipe navigation is easy. In the New Project wizard, on the dialog where we set the activity and layout name, select Scrollable Tabs + Swipe for Navigation Type as shown in the following screenshot:

The resulting project will include a layout resource file containing ViewPager and PagerTitleStrip, along with stubbed-out code for the FragmentPagerAdapter, Fragment, and Activity derived classes.