Build an Android Pager Component

One of the most important user interface paradigms for modern mobiles is their swipable multi-touch interface. The most obvious example of this is the pager view, which allows a user to swipe through pages of information intuitively. The best known example of this is the home screen of iOS, which arranges applications in a grid across multiple pages. This has proven so successful that it has been used in a variety of other places, and has been outright copied by other phone manufacturers.

When I started programming for Android, I had assumed there would be an out-of-the box component allowing me to do this, or at least one that would be easily configurable to perform a paged view. That turned out not to be the case, so I had to dive into the Android source to work out a way of implementing it (and as an aside, this is an out-of-the-box feature of iOS). I thought I’d share the fruits of my labour with you.

Investigating

At first, I started playing with ViewFlipper using Gestures to provide the input to change pages. This worked, but did not give the correct feedback to the user while scrolling. When the pager scrolls, the user expects the page to move with the swipe. With this approach, the page does not animate until the swipe gesture is completed.

Next, I toyed with the idea of writing my own scrolling component from scratch. This would give me the most flexibility, but would involve a lot of effort. It’s generally much better to build on existing components rather than write your own, so I had one last trawl through the API to see what I could use. It was then that I found HorizontalScrollView.

This acts as a traditional scroll view except it works horizontally, which is exactly what I want. It also provides key bindings for free, which is a bonus. The only missing pieces are a method of getting it to snap to the page boundary when the user releases their finger, an easy to use API for adding and removing pages, and a page indicator to show the current page. I created a subclass, and started customising it to produce the effect I wanted.

Simple Page Management

HorizontalScrollView accepts just one child view by default, and it is up to the programmer to ensure it is of the correct size. I wanted to have multiple child views, or pages, and each page had to take up the entire viewport. To do this I added an addPage() method, which would take a view, resize it to be the size of the viewport, and add it to the LinearLayout which served as the child view of the ScrollView.

public class Pager extends HorizontalScrollView {
    private LinearLayout contents;

    ...

    public void addPage(View child) {
        int width = getWidth();
        child.setLayoutParams(new LayoutParams(width, LayoutParams.FILL_PARENT));
        contents.addView(child);
        contents.requestLayout();

        firePageCountChanged();
    }

    ...

}

Snap to Page Boundary

To do this, we simply listen to touch-up events, and then issue a final smoothScrollTo() call to scroll back to the nearest page boundary

public boolean onTouchEvent(MotionEvent evt) {
    boolean result = super.onTouchEvent(evt);

    int width = getWidth();

    if (evt.getAction() == MotionEvent.ACTION_UP) {
        int pg = (getScrollX() + width / 2) / width;
        smoothScrollTo(pg * width, 0);
    }
    return result;
}

Additional code is required to handle keyboard based events in a similar fashion, which I leave as an exercise for the reader.

Pager at GitHub contains the full source code within the PagingScrollerExample.

Page Indicator

By default, the horizontal scroll pane will display a standard style scroll bar while the scroll pane is scrolling, which fades after a few seconds. This is fine under most circumstances, and is exactly what I used on NodeDroid, my usage checking application. But I thought it might be nice to have an iOS style page indicator at the bottom of the screen.

This was implemented by creating a separate custom view component to display the pages, which I called a PageIndicator. This component exists independently of the pager so that you can place the view where you wish. In your activity start up, you simply tell the page indicator which pager to listen to, and it automatically updates itself from then on.

In order for the pager to update itself properly, it needs to know when the page changes, and when pages are added or removed from the pager. Inexplicably, there is no default listener for ScrollView in Android, so the only way you can receive notification is to trap it in a subclass using the onScrollChanged method. In my code, I created a second set of Listener/Event classes called OnPageChangeListener, with two events named onPageChange() and onPageCountChange().

PageIndicator at GitHub contains the full source code within the PagingScrollerExample.

Try for Yourself

I have produced a sample application which demonstrates the use of the view, and the code is available on GitHub. Check out the PagingScrollerExample. If you’d like to try it out, clone the repo, load up the project in Eclipse (with the ADT Plugin for Eclipse installed), and you should be away.

Win an Annual Membership to Learnable,

SitePoint's Learning Platform

  • David Smith

    I like to think that my monitor is reasonably well calibrated, but your links are of a color (#A5CA39) that is almost utterly unreadable to these (admittedly somewhat elderly) eyes.

    I found the article sufficiently useful (thanks!) that it was worth the extra effort, and if I’m the only person who has commented on the link color, by all means ignore my whining, but otherwise…

    David

    • http://buildmobile.com Paul Bridgestock

      Thank you David, we have taken your link colour feedback on board, more news on that later. Very pleased the article was useful for you, do you have any special requests that you would like to learn about?

  • http://www.johnbimmerle.com John Bimmerle

    I am new to the whole web design world ( I have taken one Dreamweaver class and googled a million topics ). I am a huge Android fan and found your page through the Sitepoint newsletter. I look forward to keeping up with your content. Most of it is way beyond my skill level and understanding right now, but I hope to be able to someday gain enough knowledge to be able to start writing programs and such.

    Keep the content coming!

    • http://8bitcloud.com/ Bruce Cooper

      Hi John. If you are just getting started with Android apps, you might like to consider App Inventor (http://appinventor.googlelabs.com/about/). Its a web app that allows you to design android apps using a drag and drop interface. Its not as powerful as full fledged app development using Java, but its a great toe in the water.

      • john bimmerle

        Thanks a lot for the suggestion. I will have to take a look at it and see what I can do. I have to remind myself to take baby steps as I tend to get ahead of myself sometimes.

  • http://emile.senga.cd Emile

    This is awesome! I am looking to build a learning tool using a Pulse like UI, I think this will come in quite handy for the horizontal scrolling.

  • Gal

    Awseome post .
    I was looking for a solution like that for a while .
    This post helpd me very much.

  • Keshav

    Hi Bruce,

    That was really a nice one. I would like to share that I have achieved the above functionality with View Flipper too by overriding its onTouchEvent method. But its nice as it provides more controlls.

    Thanks for such a nice post.

    Regards,
    Keshav
    Android App Developer

  • Marcos

    Really thank you!!! I’ve been searching for something like this for months!!! Awesome!
    Really well coded. Congratulations!

  • http://androidtrainningcenter.blogspot.in/ Tofeeq

    Very nice and useful post.Keep doing this..