Skip to content

Instantly share code, notes, and snippets.

/**
* 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
class NoScrollRecyclerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0)
: RecyclerView(context, attrs, defStyleAttr) {
override fun scrollBy(x: Int, y: Int) {
val lm = layoutManager as? NoScrollHorizontalLayoutManager
lm?.canScrollHorizontally = true
super.scrollBy(x, y)
lm?.canScrollHorizontally = false
}
}
tabsRecyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
totalTabsScroll += dx
}
})
viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
// Scroll tabs as viewpager is scrolled
val dx = (position + positionOffset) * tabItemWidth - totalTabsScroll
private fun expandItem(holder: ListViewHolder, expand: Boolean, animate: Boolean) {
if (animate) {
val animator = getValueAnimator(
expand, listItemExpandDuration, AccelerateDecelerateInterpolator()
) { progress ->
holder.cardContainer.layoutParams.height =
(originalHeight + (expandedHeight - originalHeight) * progress).toInt()
holder.cardContainer.layoutParams.width =
(originalWidth + (expandedWidth - originalWidth) * progress).toInt()
cardContainer.doOnLayout { view ->
originalHeight = view.height
// show expandView and record expandedHeight in next
// layout pass (doOnNextLayout) and hide it immediately
expandView.isVisible = true
cardContainer.doOnNextLayout { view ->
expandedHeight = view.height
// We use post{} to hide the view. Otherwise the parent will not
inline fun <T : View> T.doOnGlobalLayout(crossinline action: () -> Unit) {
viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
viewTreeObserver.removeOnGlobalLayoutListener(this)
action()
}
})
}
inline fun getValueAnimator(forward: Boolean = true, duration: Long, interpolator: TimeInterpolator,
crossinline updateListener: (progress: Float) -> Unit
): ValueAnimator {
val a =
if (forward) ValueAnimator.ofFloat(0f, 1f)
else ValueAnimator.ofFloat(1f, 0f)
a.addUpdateListener { updateListener(it.animatedValue as Float) }
a.duration = duration
a.interpolator = interpolator
return a
@nikhilpanju
nikhilpanju / MainListAdapter.kt
Last active December 6, 2019 16:51
Gist to showcase expanding/collpasing items
override fun onBindViewHolder(holder: ListViewHolder, position: Int) {
val model = adapterList[position]
// set expand state on binded items because views are recycler
expandItem(holder, model == expandedModel, animate = false)
holder.cardContainer.setOnClickListener {
if (expandedModel == null) {
// expand clicked view
expandItem(holder, expand = true, animate = true)
class ToolbarBehavior : CoordinatorLayout.Behavior<AppBarLayout>() {
/** Consume if vertical scroll because we don't care about other scrolls */
override fun onStartNestedScroll(coordinatorLayout: CoordinatorLayout, child: AppBarLayout,
directTargetChild: View, target: View, axes: Int, type: Int): Boolean {
getViews(child)
return axes == ViewCompat.SCROLL_AXIS_VERTICAL ||
super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type)
}