4. Filter Sheet Open/Close Animation

This animation consists of 4 sections:

Floating Action Button (FAB) arc path animation where the button moves to the centre of the screen. Scale Down Animation which shrinks and fades all the RecyclerView items in the background. FAB Reveal Animation where the button circular reveals into a bottom sheet while the filter icon moves down. Elements Settle Animation where the tabs slide up, viewpager fades in and the bottom bar slides and fades in.

The FAB is actually just a CardView . For a lot of the animations the CardViewAnimatorHelper helper class is used. It provides a ValueAnimator making it easy to animate CardView attributes like elevation, radius, etc.

#1 FAB Arc Path Animation

CardViewAnimatorHelper is all we need for this animation. Supplying isArcPath = true takes care of arcing the path. Internally it uses ArcAnimator to get arc path values.

val pathAnimator = CardViewAnimatorHelper(

cardView = fab,

startX = fabX, startY = fabY,

endX = fabX2, endY = fabY2,

startElevation = fabElevation, endElevation = fabElevation2,

isArcPath = true,

duration = pathAnimDuration,

interpolator = pathAnimInterpolator

).getAnimator(isOpening)

#2 Scale Down Animation

All the RecyclerView items fade and scale down in the background as the arc path happens. To do this, we need to animate all the visible items in the LayoutManager . In the RecyclerView Adapter:

#3 Reveal Animation

This reveal animation does not use the Android Utils Circular Reveal because the filter icon needs to translate down simultaneously. We can do this by setting layout_gravity = bottom|center_horizontal to the icon in the CardView and calling requestLayout() when animating the CardView. Also, you may have noticed, the reveal is not a perfect circular reveal. It’s an increase in circle size of the CardView followed by un-curving the corners (radius = 0).

CardViewAnimatorHelper can fetch you an animator using getAnimator() directly or you can set progress to it for manual control. We use the latter method here.

#4 Settle Animation

This animation is nothing special. It’s just views fading, sliding and settling in. But what’s interesting is, although it may seem fluid coming off of the reveal animation, it isn’t.

The reveal animation was done on the fab CardView . But once the reveal is done, another ViewGroup main_container which has a higher elevation than the fab is made visible and the settle animation happens on the elements in that ViewGroup.

After the reveal animation, main_container is made visible and picks up the animation from there. The reverse is done when closing the filter sheet. This was done because if all the elements were in the fab CardView , it’s not easy to animate them considering how the fab grows and shrinks in size (*cough* MotionLayout *cough*).

if (isOpening) revealAnimator.doOnEnd { mainContainer.isVisible = true }

else revealAnimator.doOnStart { mainContainer.isVisible = false }

Note that the bottom bar is a separate CardView. The fab and the bottom bar being separate CardViews helps with the next animation (explained soon).

Choroeographing animation with AnimatorSet

I’m not too fond of the AnimatorSet API because of some of it’s gotchas and other reasons. (Check out Chris Banes’s post on using coroutines with animators). But here’s how it’s done for this animation: