Animation / AppBar / AppBarLayout / Material Design

AppBar – Part 2

The AppBar is an evolution of the ActionBar which provides us with a rich toolbox for defining and customising the behaviour. All apps will have different requirements for their AppBars and each app may have different AppBar implementations in different Activities. In this series, rather than look at the AppBar implementations themselves, we’re going to explore how can integrate and animate different elements of the AppBar to provide smooth UI and UX which implement material design style animations and transitions.

The next thing we’re going to turn our attention to is changing the feature image in out collapsing Toolbar when the user swipes between pages in the ViewPager. The first thing we need to do is update the PagerAdapter to enable us to look up an appropriate image resource ID for each page:

I won’t give a detailed explanation of this because it is a pretty basic PagerAdapter implementation – we’re just adding a method named getImageId which will return a resource ID of the image for that position. We could have got away with using a Pair<String, Integer>Section class, but we’re going to be adding further to this, so I’ve opted to use a class instead.

The next thing we need to do is change our layout to add a second image so that we can transition between the image for the outgoing page and the incoming page. This second image will only ever be visible during the actual transitions:

Next we need to add a ViewPager.OnPageChangeListener which will track the movement between pages, and trigger and manage the appropriate transitions:

The main workhorse here is the onPageScrolled() method which will be called whenever the ViewPager scroll position changes in response to a drag or swipe by the user. It essentially manages a state machine whose state changes depending on whether a scroll is in progress or not. The first three conditionals check for changes in state, and the final two apply an appropriate movement depending on the direction of scrolling.

There are a number of methods which simply encapsulate some boolean logic which return things like whether the scroll has finished, or the scroll direction. These are simply to improve readability of the code – the actual boolean logic itself requires some study and understanding, so by replacing this with a meaningful method name instead of the raw logic it make it easier to understand the flow through onPageScrolled().

Similarly there are two methods for starting and stopping the scroll which set the state appropriately.

Finally there are a couple more interface methods – onPageScrollStateChanged() which we’re not interested in because we are detecting state changes ourself. The reason for this is handling some edge cases where the user begins dragging in one direction, and then changes direction without lifting.

The final method is onPageSelected() which gets called if the user taps on one of the tabs rather than dragging or swiping the ViewPager. In this case we actually want to smoothly transition from the image for the start position directly to the image in the end position. If we were to omit this, we might see a strobing effect when we pass though many different positions because we would load each of the images in the positions between the start and end position.

An alternative approach for managing transition is to use ViewPager.PageTransformer instead of ViewPager.OnPageChangeListener. The reason I have opted for this approach is that PageTransformer is well suited for handling transitions between the different pages within the ViewPager itself, but OnPageChangeListener gives us a little finer-grained control when it comes to applying the transition to object external to the ViewPager.

So PageChangeListener is responsible for managing the scroll state, but does not actually perform the transitions. This separation of concerns is useful because we can reuse this logic an just apply different ImageAnimator implementations – and it’s the ImageAnimator which actually manages the transition itself. So let’s take a look at this:

This will perform an alpha fade between two images.The outgoing image is drawn behind the incoming image because of the positioning within the layout, and we simply change the alpha value of the incoming image depending on the position within the scroll. start() and end() are called to intialise and clean things up at the start and end of the scroll. Then forwards() and backwards() are called to either fade in or out depending on the direction of scroll. There is a small piece of complexity because of the case we discussed previously where we may be traversing a number of separate positions when the user tabs on a tap rather than swiping, but the getOffset() method maps the position & positionOffset values to a simple float with a range of 0.0-1.0 where 0.0 is the start position, 1.0 is the end position and intermediate values are the mid points during the transition.

All that remains is to actually hook up PageChangeListener:

If we run this we can see that we get a nice alpha fade between the images when we drag and the transition follows the movement of the drag, and we also get a smooth transition when tapping the tabs:

That’s nice – but we can do better. In the next article we’ll look at adding some motion to the transition.

The source code for this article is available here.

© 2015, Mark Allison. All rights reserved.

CC BY-NC-SA 4.0 AppBar – Part 2 by Styling Android is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. Permissions beyond the scope of this license may be available at http://blog.stylingandroid.com/license-information.

1 Comment

Leave a Reply

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