Skip to content

Commit

Permalink
Merge pull request #52 from st235/fix/scrolling_behaviour
Browse files Browse the repository at this point in the history
fixed scrolling behaviour for corrdinator layout.
  • Loading branch information
st235 authored Aug 24, 2020
2 parents 5c5cc95 + ec32fd6 commit 3947d16
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 30 deletions.
44 changes: 21 additions & 23 deletions app/src/main/res/layout/activity_scrollable_coordinator_layout.xml
Original file line number Diff line number Diff line change
@@ -1,32 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="github.com.st235.expandablebottombar.screens.CoordinatorLayoutActivity">
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="github.com.st235.expandablebottombar.screens.CoordinatorLayoutActivity">

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />

<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
app:srcCompat="@drawable/ic_meow"/>
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
app:srcCompat="@drawable/ic_meow" />

<github.com.st235.lib_expandablebottombar.ExpandableBottomBar
android:id="@+id/expandable_bottom_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:layout_behavior="github.com.st235.lib_expandablebottombar.behavior.ExpandableBottomBarScrollableBehavior"
app:exb_items="@menu/bottom_bar" />
android:id="@+id/expandable_bottom_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:exb_items="@menu/bottom_bar"
app:layout_behavior="github.com.st235.lib_expandablebottombar.behavior.ExpandableBottomBarScrollableBehavior" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
ext {
vers = [
versionCode: 35,
versionName: "1.2.2"
versionCode: 36,
versionName: "1.2.3"
]
info = [
name: 'expandablebottombar',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
package github.com.st235.lib_expandablebottombar.behavior

import android.animation.ValueAnimator
import android.content.Context
import android.os.Handler
import android.os.Looper
import android.util.AttributeSet
import android.view.View
import android.view.animation.DecelerateInterpolator
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.ViewCompat
import androidx.core.view.marginBottom
import github.com.st235.lib_expandablebottombar.utils.clamp
import kotlin.math.abs

class ExpandableBottomBarScrollableBehavior<V: View>:
ExpandableBottomBarBehavior<V> {

private val handler = Handler(Looper.getMainLooper())
private var lastKnownRunnable: Runnable? = null

private var animator: ValueAnimator? = null
private var lastKnownDirection: Int? = null

constructor(): super()

constructor(context: Context, attributeSet: AttributeSet): super(context, attributeSet)
Expand All @@ -20,10 +32,77 @@ class ExpandableBottomBarScrollableBehavior<V: View>:
return axes == ViewCompat.SCROLL_AXIS_VERTICAL
}

override fun onNestedPreScroll(
coordinatorLayout: CoordinatorLayout, child: V, target: View, dx: Int, dy: Int, consumed: IntArray, type: Int
) {
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type)
child.translationY = clamp(child.translationY + dy, 0f, child.height.toFloat())
override fun onNestedScroll(coordinatorLayout: CoordinatorLayout, child: V, target: View, dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int, type: Int, consumed: IntArray) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type, consumed)

if (lastKnownRunnable != null) {
handler.removeCallbacks(lastKnownRunnable)
lastKnownRunnable = null
}

cancelAnimation()

lastKnownDirection = dyConsumed
child.translationY = getScrollRange(child, dyConsumed)
}

override fun onStopNestedScroll(coordinatorLayout: CoordinatorLayout, child: V, target: View, type: Int) {
super.onStopNestedScroll(coordinatorLayout, child, target, type)

if (lastKnownRunnable != null) {
handler.removeCallbacks(lastKnownRunnable)
}

lastKnownRunnable = Runnable {
animateWithDirection(child)
}
handler.postDelayed(lastKnownRunnable, 500L)
}

private fun getScrollRange(child: V, dy: Int): Float {
return clamp(child.translationY + dy, 0f, getMaxScrollDistance(child))
}

private fun getMaxScrollDistance(child: V): Float {
val childHeight = if (ViewCompat.isLaidOut(child)) child.height else child.measuredHeight
return childHeight.toFloat() + child.marginBottom
}

private fun animateWithDirection(child: V) {
val dy = lastKnownDirection ?: return
val halfOfScrollDistance = getMaxScrollDistance(child) / 2F

val overHalfUp = dy < 0 /* up */ && abs(child.translationY) < halfOfScrollDistance
val lessThanHalfDown = dy > 0 /* down */ && abs(child.translationY) < halfOfScrollDistance

if (overHalfUp || lessThanHalfDown) {
animateTo(child, 0F)
} else {
animateTo(child, getMaxScrollDistance(child))
}
}

private fun animateTo(child: V, translation: Float) {
if (child.translationY == 0F || child.translationY == getMaxScrollDistance(child)) {
return
}

cancelAnimation()

animator = ValueAnimator.ofFloat(child.translationY, translation)
animator?.interpolator = DecelerateInterpolator()
animator?.addUpdateListener { animator ->
val animatedValue = this.animator?.animatedValue as Float
child.translationY = animatedValue
}

animator?.start()
}

private fun cancelAnimation() {
if (animator != null && animator?.isRunning == true) {
animator?.cancel()
animator = null
}
}
}

0 comments on commit 3947d16

Please sign in to comment.