Animation / ViewPager / ViewPagerAnimator

ViewPagerAnimator – The Basics

I am delighted to announce the release of a new lightweight, yet extremely powerful ViewPager animation library named, unsurprisingly ViewPagerAnimator. ViewPagerAnimator is designed to animate arbitrary values as the user navigates between pages within a ViewPager, and will precisely follow the motion of h[is|er] finger. There is nothing groundbreaking in terms of the techniques I’m using to do this – I previously did something very similar in Prism. However, there are some very nice subtleties in terms of the API design.


Let’s begin by defining ‘lightweight’. ViewPagerAnimator consists of one class (160 lines) and two interfaces, each containing a single method. The AAR clocks in at 19KB and 16 methods. But for such a small package it packs a lot of punch.

Of the 17 methods, 6 are actually strongly typed, static factory methods, and half of this are package private versions which simplify testing. Of the remaining 11 methods there are only really 2 that ever need calling. The first is to specify an Interpolator to use for the animation, and the second is a destroy method which will perform some tidy-up and should be called to detach the ViewPagerAnimator from its ViewPager.

So, let’s take a look at how we actually use it:

public class MainActivity extends AppCompatActivity {
    private PagerAdapterFactory factory;
    private ViewPagerAnimator animator;

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

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        factory = new PagerAdapterFactory(this);

        ViewPager viewPager = setupViewpager();
        TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);

        tabLayout.setupWithViewPager(viewPager);
    }

    private ViewPager setupViewpager() {
        final ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
        FragmentManager fragmentManager = getSupportFragmentManager();
        final PagerAdapter pagerAdapter = factory.getPagerAdapter(fragmentManager);
        viewPager.setAdapter(pagerAdapter);

        Provider<Integer> provider = new Provider<Integer>() {
            @Override
            public Integer get(int position) {
                return pagerAdapter.getColour(position);
            }
        };
        Property<Integer> property = new Property<Integer>() {
            @Override
            public void set(Integer value) {
                viewPager.setBackgroundColor(value);
            }
        };

        animator = ViewPagerAnimator.ofArgb(viewPager, provider, property);

        return viewPager;
    }

    @Override
    protected void onDestroy() {
        animator.destroy();
        super.onDestroy();
    }
}

All of the work is being done in the marked section. We have initialised a ViewPager with a PagerAdapter, and we’re good to go. The first thing that we need to do is create implementations of the Provider and Property interfaces. Both of these enable us to create a facade to another class in order to expose methods without ViewPagerAnimator requiring any knowledge of those classes. A Provider will provide a value for different page positions within the ViewPager. An obvious candidate for this will always be the PagerAdapter implementation as this contains the state of the various pages. So in our implementation we can create a Provider instance which retrieves an Integer value for a given pager position. Provider is a generic interface so we can specify different return types dynamically:

Provider<Integer> provider = new Provider<Integer>() {
    @Override
    public Integer get(int position) {
        return pagerAdapter.getColour(position);
    }
};

The highlighted line shows how we simply call the correct method to obtain the value we need. Although we require knowledge of the PagerAdapter here, where we create this instance, when we pass the Provider instance in to ViewPagerAnimator, it is completely decoupled from the PagerAdapter, all it knows about is the Provider, and calls its get(int position) method in order to obtain values for each pager position.

The next thing that we need to do is create a Property instance. Property is very similar to Provider because it too acts as a facade. But where Provider is used to retrieve a value, Property is used to set a value. So essentially Provider is a getter facade and Property is a setter facade. We’ll create a Property which will set the background colour of the ViewPager:

Property<Integer> property = new Property<Integer>() {
    @Override
    public void set(Integer value) {
        viewPager.setBackgroundColor(value);
    }
};

Once again, it is important that the ViewPagerAnimator has no knowledge that it is the ViewPager, or even the background colour that it is setting. It only has knowledge of the Property.

The other important thing here is that the generic types of both the Provider and the Property must match. In this case we are dealing with colour values which are represented as Integers. The Provider is returning a different colour value for each ViewPager position, the ViewPagerAnimator will track the movement between pages, calculate an intermediate value and set a colour value on the Property.
So to set all of this up all we need to do is:

animator = ViewPagerAnimator.ofArgb(viewPager, provider, property);

ofArgb() is a strongly-typed factory method which requires strongly-typed arguments of Provider and Property. If you attempt to mix generic types, the compiler will give an error. So you will see any type mismatch issues in the IDE.

So, if we have a PagerAdapter with three pages which returns the colours red (0xFF0000), green (0x00FF00), and blue (0x0000FF) for the respective positions, then we can see how ViewPagerAnimator gives us really smooth transitions between the colours as we navigate though the pages:

Please forgive the quality of the GIF, it’s got some artefacts going on. I assure you it is much smoother on a real device, but the GIF should give an idea of what ViewPagerAnimator is doing for us.

But we’re not done yet. While the basic usage of ViewPagerAnimator there are some further subtleties that we’ll look at in the next article.

ViewPagerAnimator is available now on jcenter compile 'com.stylingandroid.viewpageranimator:viewpageranimator:1.0.1' and the source is available here.

© 2017, Mark Allison. All rights reserved.

Copyright © 2017 Styling Android. All Rights Reserved.
Information about how to reuse or republish this work may be available at http://blog.stylingandroid.com/license-information.

4 Comments

  1. I really like the idea and API! Is Provider compatible with Provider used by dagger (like autofactory does)? That would make using it with dagger way simpler and would result in library usage (almost) one liner (except for dependency code)

    1. No `Provider` has not been designed for parity with Dagger. You could, possible, adapt it, though

  2. This library looks very interesting. However, when I add it as a dependency I can build the app without problem – unfortunately Android Studio doesn’t see any class included in this library (it is not even located in third party libs folder). I haven’t experienced this issue before with any other library. Do you have some idea how to solve it? Thanks

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.