Animation / ViewPager / ViewPagerAnimator

ViewPagerAnimator – The Advanced Stuff

Earlier this month I released 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.


Previously we saw how we can create a colour transition between pages by implementing a Provider, a Property, and use these as facades for ViewPagerAnimator to perform the necessary animations. But we are not just limited to Argb colour values, there are also strongly-typed factory methods which will animate Integer or Float values as well.

If that was not enough, it is also possible to animate any arbitrary types by using calling the Constructor directly:

Provider<MyType> provider = new Provider<MyType>() {
    @Override
    public MyType get(int position) {
        return null; // Actually return a MyType object here
    }
};
Property<MyType> property = new Property<MyType>() {
    @Override
    public void set(MyType value) {
        // Set MyType object here
    }
};

TypeEvaluator<MyType> evaluator = new TypeEvaluator<MyType>() {
    @Override
    public MyType evaluate(float fraction, MyType startValue, MyType endValue) {
        return null; //Actually calculate the fractional value
    }
};

ViewPagerAnimator<MyType> myTypeAnimator = new ViewPagerAnimator<>(viewPager,provider, property, evaluator);

So MyType can actually be anything you like, but you’ll need to provide a TypeEvaluator instance which can calculate a fractional value between a start and end point. Take a look at the cource for IntEvaluator, FloatEvaluator ArgbEvaluator, and RectEvaluator for some concrete examples of how to implement a TypeEvaluator for an arbitrary type.

Once again we get the same strict type-checking when we use the ViewPagerAnimator constructor directly.

So that’s pretty nice – we can basically use ViewPagerAnimator to animate any arbitrary type that we like, but things don’t stop there. Some of the real power of this API design comes to the fore once we leverage the power of some of the new features in Java 8. At the time of writing, Java 8 support is available on Android Studio 2.4 alpha6 and the associated gradle plugin. It’s not quite ready for mainstream yet, but it’s not far off, so we’ll look at how it further improves the fluency of our code. Let’s start by returning to our Java 7 example code:

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);

If we turn on Java 8 support (I won’t cover that here as full details of hot to enable Java 8 can be found here), we will instantly get suggestions from Android Studio that we can replace the Provider and Property instantiation with lambdas. If we allow Android Studio to do that for us, we get much terser code which is easier to read an uderstand:

Provider<Integer> provider = position -> pagerAdapter.getColour(position);
Property<Integer> property = value -> viewPager.setBackgroundColor(value);

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

We can inline these to get everything on to a single line:

animator = ViewPagerAnimator.ofArgb(viewPager, position -> pagerAdapter.getColour(position), value -> viewPager.setBackgroundColor(value));

So lambdas, which are a fairly well known feature of Java 8 really help to remove some of the boilerplate. But there is something else which we can leverage which will simplify things even further: method references. A method reference is a mechanism to allow us to pass in a specific method of an object, and is made possible by the design of the Provider and Property interfaces, each containing a single method. Through this basic design we can pass in any method which matches the method signature of those single methods in the interface. Android Studio will also suggest converting our newly created lambdas to method references. If we allow it to perform it’s magic, then we’re left with this:

animator = ViewPagerAnimator.ofArgb(viewPager, pagerAdapter::getColour, viewPager::setBackgroundColor);

So we pass in a reference to the methods of specific objects which match the method signatures defined in our two interfaces, and get en extremely terse, yet easy to read, and very powerful mechanism for loosely-coupled delegation.

It is worth pausing for a minute to understand what is happening here. the ViewPagerAnimator code is fully Java 7 compliant – there are no Java 8 dependencies in there whatsoever. But with careful API design we can create an API which will leverage Java 8 features, if available.

So while ViewPagerAnimator might be useful for some, the bit that is really interesting is seeing how we can really utilise some of the power of Java 8 with some intelligent API design.

The final post in this series will cover the internals and give an explanation of how it actually works. However, because next week is Google IO 2017, it will follow in two weeks time. Next week’s post will be something a little different, but hopefully very interesting and useful.

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.

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.