Skip to content

Instantly share code, notes, and snippets.

@nikhilpanju
Created December 4, 2019 07:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nikhilpanju/e765a43230bd08c3b99feb56b5e8b6a4 to your computer and use it in GitHub Desktop.
Save nikhilpanju/e765a43230bd08c3b99feb56b5e8b6a4 to your computer and use it in GitHub Desktop.
/**
* Called when fab is clicked to open filter sheet (open = true)
* or called when closeIcon is clicked to close the filter sheet (open = false)
*
* It's a 3 step animation played in sequence. Read the comments to know more.
*/
private fun openFilterSheet(open: Boolean) {
// 1) Fab Arc Path Animator
// Animates path of the fab from starting position to centre of screen
val pathAnimator = CardViewAnimator(
cardView = fab, startX = fabX, startY = fabY, endX = fabX2, endY = fabY2,
startElevation = fabElevation, endElevation = fabElevation2, isArcPath = true,
duration = pathAnimDuration, interpolator = getPathAnimationInterpolator(open)
).getAnimator(open)
// 2) Scale Down Animation
// Happens simultaneously to the path animation
pathAnimator.doOnStart { (context as MainActivity).animateMainListAdapter(open) }
// 3) Reveal Animator
// Fab expands outwards to reveal the filter layout. It's just a trick.
// After expanding, the mainContainer is made visible (it has higher elevation so
// that it sits on top of the fab). To the user, it looks seamless
// 0f -> 0.8f: Increase size of fab 0.8f -> 1f: Un-curve corners of fab
val prop1 = CircleCardViewAnimator(
fab, startSize = fabSize, endSize = sheetWidth, startX = fabX2, startY = fabY2)
val prop2 = CardViewAnimator(fab, startRadius = sheetWidth / 2, endRadius = 0f)
val revealAnimator = getValueAnimator(
open, revealAnimationDuration, AccelerateDecelerateInterpolator()) { progress ->
if (progress <= 0.8f) {
prop1.progress = progress / 0.8f
} else {
if (prop1.progress != 1f) prop1.progress = 1f
prop2.progress = (progress - 0.8f) / 0.2f
}
}
// mainContainer contains all the filter sheet views. So when revealing or un-revealing
// we need to show/hide the mainContainer because the fab is behind (elevation) the mainContainer
// and we want to seamlessly transition from the fab to the mainContainer
if (open) revealAnimator.doOnEnd { mainContainer.isVisible = true }
else revealAnimator.doOnStart { mainContainer.isVisible = false }
// 4) Settle Animator - All the elements of the filter screen settle into place
// Since mainContainer sits on top of the fab, we can fade in all the elements of mainContainer
// and the bottom bar
val settleAnimator = getValueAnimator(open, settleAnimDuration,
if (open) DecelerateInterpolator() as TimeInterpolator else AccelerateInterpolator()) { progress ->
// 4a) Bottom bar bg is faded in for it to stand out
bottomBarCardView.setCardBackgroundColor(
blendColors(bgColor, if (hasActiveFilters) bottomBarPinkColor else bottomBarColor, progress))
// 4b) Bottom bar slides in from left
bottomBarContainer.translationX = -bottomBarTranslateAmount * (1 - progress)
// 4c) Filter icon fades to white as it settles
filterIcon.setColorFilter(blendColors(filterIconColor, filterIconActiveColor, progress))
// 4d) Close icon fades in
closeIcon.alpha = progress
// 4e) Tabs slide up (Increasing height of filterContainer and tabsContainer, translating the
// tabsRecyclerView up to make it look more natural)
val height = tabsHeight * progress
filtersContainer.layoutParams.height = (sheetHeight + height).toInt()
tabsContainer.layoutParams.height = height.toInt()
tabsRecyclerView.translationY = tabsHeight * (1 - progress)
filtersContainer.requestLayout()
// 4f) Sliding up the ViewPager
viewPager.translationY = (1 - progress) * viewPagerTranslateAmount
viewPager.alpha = progress
}
// Animator Set choreographs all 3 animations based on the `open` variable.
// If it's closing, we tell the activity, it's closed so that it can show its fab and remove
// this fragment
val set = AnimatorSet()
if (open) {
set.playSequentially(pathAnimator, revealAnimator, settleAnimator)
} else {
set.playSequentially(settleAnimator, revealAnimator, pathAnimator)
// Remove adapters after filter sheet is closed
set.doOnEnd { setAdapters(false) }
}
set.start()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment