Tutorial #10 - Rigging Up a ViewPager

A ViewPager is a fairly slick way to present a digital book. You can have individual portions of the book be accessed by horizontal swiping, with the prose within a portion accessed by scrolling vertically. While not offering “page-at-a-time” models used by some book reader software, it is much simpler to set up.

So, that’s the approach we will use with EmPubLite. Which means, among other things, that we need to add a ViewPager to the app.

This is a continuation of the work we did in the previous tutorial.

You can find the results of the previous tutorial and the results of this tutorial in the book’s GitHub repository.

Step #1: Add a ViewPager to the Layout

Right now, the main layout used by EmPubLiteActivity just has a TextView. We need to change that to have our ViewPager.

Since ViewPager is not available for drag-and-drop through the IDE graphical layout editors, even IDE users are going to have to dive into the layout XML this time.

Open up res/layout/main.xml and switch to the Text sub-tab to see the raw XML. Replace its contents with:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical">

  <io.karim.MaterialTabs
    android:id="@+id/tabs"
    android:layout_width="match_parent"
    android:layout_height="48dp"
    app:mtIndicatorColor="@color/colorAccent"
    app:mtSameWeightTabs="true"/>

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

(from EmPubLite-AndroidStudio/T10-ViewPager/EmPubLite/app/src/main/res/layout/main.xml)

This adds our ViewPager, underneath a MaterialTabs, inside a vertical LinearLayout. MaterialTabs is an implementation of tabs for a ViewPager, one that we will explore in greater detail later in the book.

Step #2: Creating a ContentsAdapter

A ViewPager needs a PagerAdapter to populate its content, much like a ListView needs a ListAdapter. We cannot completely construct a PagerAdapter yet, as we still need to learn how to load up our book content from files. But, we can get part-way towards having a useful PagerAdapter now.

Right-click over the com.commonsware.empublite package in your java/ directory and choose New > Java Class from the context menu. Fill in ContentsAdapter as the name and click OK to create the empty class.

Then, replace the generated ContentsAdapter.java file with the following content:

package com.commonsware.empublite;

import android.app.Activity;
import android.app.Fragment;
import android.support.v13.app.FragmentStatePagerAdapter;

public class ContentsAdapter extends FragmentStatePagerAdapter {
  public ContentsAdapter(Activity ctxt) {
    super(ctxt.getFragmentManager());
  }

  @Override
  public Fragment getItem(int arg0) {
// TODO Auto-generated method stub
    return null;
  }

  @Override
  public int getCount() {
// TODO Auto-generated method stub
    return 0;
  }
}
(from EmPubLite-AndroidStudio/T10-ViewPager/EmPubLite/app/src/main/java/com/commonsware/empublite/ContentsAdapter.java)

If you prefer, you can view this file’s contents in your Web browser via this GitHub link.

Step #3: Setting Up the ViewPager

Now, we need to add some code to retrieve the ViewPager, populate it with the ContentsAdapter, and do something useful with those tabs.

First, add two fields to EmPubLiteActivity:

  private ViewPager pager;
  private ContentsAdapter adapter;

(from EmPubLite-AndroidStudio/T10-ViewPager/EmPubLite/app/src/main/java/com/commonsware/empublite/EmPubLiteActivity.java)

This will require adding an import for android.support.v4.view.ViewPager.

Then, add a few more lines to the bottom of onCreate() of EmPubLiteActivity:

    pager=(ViewPager)findViewById(R.id.pager);
    adapter=new ContentsAdapter(this);
    pager.setAdapter(adapter);

    MaterialTabs tabs=(MaterialTabs)findViewById(R.id.tabs);
    tabs.setViewPager(pager);

(from EmPubLite-AndroidStudio/T10-ViewPager/EmPubLite/app/src/main/java/com/commonsware/empublite/EmPubLiteActivity.java)

This will require an import statement for io.karim.MaterialTabs.

What we are doing is:

At this point, your EmPubLiteActivity should look something like:

package com.commonsware.empublite;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import android.view.Menu;
import android.view.MenuItem;
import io.karim.MaterialTabs;

public class EmPubLiteActivity extends Activity {
  private ViewPager pager;
  private ContentsAdapter adapter;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    pager=(ViewPager)findViewById(R.id.pager);
    adapter=new ContentsAdapter(this);
    pager.setAdapter(adapter);

    MaterialTabs tabs=(MaterialTabs)findViewById(R.id.tabs);
    tabs.setViewPager(pager);
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.options, menu);

    return(super.onCreateOptionsMenu(menu));
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
      case R.id.about:
        Intent i=new Intent(this, SimpleContentActivity.class);
        startActivity(i);

        return(true);

      case R.id.help:
        i=new Intent(this, SimpleContentActivity.class);
        startActivity(i);

        return(true);
    }

    return(super.onOptionsItemSelected(item));
  }
}

(from EmPubLite-AndroidStudio/T10-ViewPager/EmPubLite/app/src/main/java/com/commonsware/empublite/EmPubLiteActivity.java)

The net effect, if you run this modified version of the app, is that we have a big blank area, taken up by our empty ViewPager:

EmPubLite, With Empty ViewPager
Figure 257: EmPubLite, With Empty ViewPager

The ViewPager is empty simply because our ContentsAdapter returned 0 from getCount(), indicating that there are no pages to be displayed.

In Our Next Episode…

… we will finish our “help” and “about” screens in our tutorial project.