The following is the first few sections of a chapter from The Busy Coder's Guide to Android Development, plus headings for the remaining major sections, to give you an idea about the content of the chapter.


Swiping with ViewPager

Android, over the years, has put increasing emphasis on UI design and having a fluid and consistent user experience (UX). While some mobile operating systems take “the stick” approach to UX (forcing you to abide by certain patterns or be forbidden to distribute your app), Android takes “the carrot” approach, offering widgets and containers that embody particular patterns that they espouse. The action bar, for example, grew out of this and is now the backbone of many Android activities.

Another example is the ViewPager, which allows the user to swipe horizontally to move between different portions of your content. However, ViewPager is not distributed as part of the firmware, but rather via the Android Support package. Hence, even though ViewPager is a relatively new widget, you can use it on Android 1.6 and up.

This chapter will focus on where you should apply a ViewPager and how to set one up.

Pieces of a Pager

AdapterView classes, like ListView, work with Adapter objects, like ArrayAdapter. ViewPager, however, is not an AdapterView, despite adopting many of the patterns from AdapterView. ViewPager, therefore, does not work with an Adapter, but instead with a PagerAdapter, which has a slightly different API.

Android ships two PagerAdapter implementations in the Android Support package: FragmentPagerAdapter and FragmentStatePagerAdapter. The former is good for small numbers of fragments, where holding them all in memory at once will work. FragmentStatePagerAdapter is for cases where holding all possible fragments to be viewed in the ViewPager would be too much, where Android will discard fragments as needed and hold onto the (presumably smaller) states of those fragments instead.

Paging Fragments

The simplest way to use a ViewPager is to have it page fragments in and out of the screen based on user swipes.

To see this in action, this section will examine the ViewPager/Fragments sample project.

The project has a dependency on the Android Support package, in order to be able to use ViewPager. In Android Studio, this is a implementation statement in the dependencies closure of build.gradle:

dependencies {
  implementation 'com.android.support:support-v13:21.0.3'
}

The Activity Layout

The layout used by the activity just contains the ViewPager. Note that since ViewPager is not in the android.widget package, we need to fully-qualify the class name in the element:

<?xml version="1.0" encoding="utf-8"?>
<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.ViewPager>

Note that ViewPager is not available for drag-and-drop in the IDE graphical designers, probably because it comes from the Android Support package and therefore is not available to all projects.

The Activity

As you see, the ViewPagerFragmentDemoActivity itself is blissfully small:

package com.commonsware.android.pager;

import android.app.Activity;
import android.os.Bundle;
import android.support.v4.view.ViewPager;

public class ViewPagerFragmentDemoActivity extends Activity {
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    ViewPager pager=(ViewPager)findViewById(R.id.pager);

    pager.setAdapter(new SampleAdapter(getFragmentManager()));
  }
}

All we do is load the layout, retrieve the ViewPager via findViewById(), and provide a SampleAdapter to the ViewPager via setAdapter().

The PagerAdapter

Our SampleAdapter inherits from FragmentPagerAdapter and implements two required callback methods:

package com.commonsware.android.pager;

import android.app.Fragment;
import android.app.FragmentManager;
import android.support.v13.app.FragmentPagerAdapter;

public class SampleAdapter extends FragmentPagerAdapter {
  public SampleAdapter(FragmentManager mgr) {
    super(mgr);
  }

  @Override
  public int getCount() {
    return(10);
  }

  @Override
  public Fragment getItem(int position) {
    return(EditorFragment.newInstance(position));
  }
}

Here, we say that there will be 10 pages total, each of which will be an instance of an EditorFragment. In this case, rather than use the constructor for EditorFragment, we are using a newInstance() factory method. The rationale for that will be explained in the next section.

The Fragment

EditorFragment will host a full-screen EditText widget, for the user to enter in a chunk of prose, as is defined in the res/layout/editor.xml resource:

<EditText xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/editor"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:inputType="textMultiLine"
  android:gravity="left|top"
  />

We want to pass the position number of the fragment within the ViewPager, simply to customize the hint displayed in the EditText before the user types in anything. With normal Java objects, you might pass this in via the constructor, but it is not a good idea to implement a constructor on a Fragment. Instead, the recipe is to create a static factory method (typically named newInstance()) that will create the Fragment and provide the parameters to it by updating the fragment’s “arguments” (a Bundle):

  static EditorFragment newInstance(int position) {
    EditorFragment frag=new EditorFragment();
    Bundle args=new Bundle();

    args.putInt(KEY_POSITION, position);
    frag.setArguments(args);

    return(frag);
  }

You might be wondering why we are bothering with this Bundle, instead of just using a regular data member. The arguments Bundle is part of our “saved instance state”, for dealing with things like screen rotations — a concept we will get into later in the book. For the moment, take it on faith that this is a good idea.

In onCreateView() we inflate our R.layout.editor resource, get the EditText from it, get our position from our arguments, format a hint containing the position (using a string resource), and setting the hint on the EditText:

  @Override
  public View onCreateView(LayoutInflater inflater,
                           ViewGroup container,
                           Bundle savedInstanceState) {
    View result=inflater.inflate(R.layout.editor, container, false);
    EditText editor=(EditText)result.findViewById(R.id.editor);
    int position=getArguments().getInt(KEY_POSITION, -1);

    editor.setHint(String.format(getString(R.string.hint), position + 1));

    return(result);
  }

The Result

When initially launched, the application shows the first fragment:

ViewPager on Android 4.3, Showing First Editor
Figure 244: ViewPager on Android 4.3, Showing First Editor

However, you can horizontally swipe to get to the next fragment:

A ViewPager in Use on Android 4.0.3
Figure 245: A ViewPager in Use on Android 4.0.3

Swiping works in both directions, so long as there is another page in your desired direction.

Paging Other Stuff

The preview of this section was whisked away by a shark-infested tornado.

Indicators

The preview of this section is [REDACTED].

Revisiting the Containers Sampler

The preview of this section was fed to a gremlin, after midnight.