Created
December 4, 2019 07:07
-
-
Save nikhilpanju/e765a43230bd08c3b99feb56b5e8b6a4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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