1+ package com.prolificinteractive.anchoredbehavior
2+
3+ import android.support.design.widget.CoordinatorLayout
4+ import android.util.Log
5+ import android.view.View
6+ import android.view.ViewGroup
7+ import android.view.animation.DecelerateInterpolator
8+ import android.view.animation.Interpolator
9+
10+ /* *
11+ * Behavior for a view to be anchored and animated according to a moving dependency.
12+ *
13+ * @param interpolator Interpolator for the animation.
14+ * @param animationHeight Height that the animation should be perform between.
15+ */
16+ open class AnchoredBehavior <T : View >(
17+ private val animationHeight : Int ,
18+ var interpolator : Interpolator = DecelerateInterpolator (2f)
19+ ) : CoordinatorLayout.Behavior<T>() {
20+
21+ private var prev: Int = 0
22+
23+ override fun onDependentViewChanged (
24+ parent : CoordinatorLayout ,
25+ child : T ,
26+ dependency : View
27+ ): Boolean {
28+
29+ val diffWidth = parent.width - dependency.width
30+ val coefY = coefficient(parent.height - child.height, child.top)
31+ val added = applyCoefWithInterpolation(diffWidth, coefY)
32+
33+ if (added != prev) {
34+ // New added size is different, button size should change.
35+ prev = added
36+ animate(parent, child, dependency, added, coefY)
37+ return true
38+ }
39+ return false
40+ }
41+
42+ /* *
43+ * Animate the child view.
44+ *
45+ * @param added Margin of the button calculated based on the coefficient.
46+ * @param coefficient Coefficient to apply based on the Y position of the dependency.
47+ */
48+ open fun animate (
49+ parent : CoordinatorLayout ,
50+ child : T ,
51+ dependency : View ,
52+ added : Int ,
53+ coefficient : Float
54+ ) {
55+ val params = child.layoutParams as CoordinatorLayout .LayoutParams
56+ val dependencyParams = dependency.layoutParams as ViewGroup .MarginLayoutParams
57+
58+ // Add margin to layout.
59+ params.width = parent.width - added
60+
61+ params.leftMargin = applyCoefWithInterpolation(dependencyParams.leftMargin, coefficient)
62+ params.rightMargin = applyCoefWithInterpolation(dependencyParams.rightMargin, coefficient)
63+
64+ child.layoutParams = params
65+ }
66+
67+ /* *
68+ * Apply an interpolation to a coefficient and get the result value once the coefficient applied.
69+ *
70+ * @param value The value to apply the coefficient to.
71+ * @param coefficient The coefficient input for the interpolation.
72+ * @return Return the value with coefficient and interpolation applied.
73+ */
74+ fun applyCoefWithInterpolation (value : Int , coefficient : Float ): Int {
75+ // The interpolation output needs to be reversed.
76+ return value - (value * (1f - interpolator.getInterpolation(1f - coefficient))).toInt()
77+ }
78+
79+ /* *
80+ * Calculate coefficient based on position of the view ansd the dependency. It's also using the
81+ * [:animationHeight][AnchoredBehavior] for the height that the button should animate.
82+ *
83+ * @param childDefaultYPosition Child position on the Y axis.
84+ * @param dependencyYPosition Dependency position on the Y axis.
85+ * @return Coefficient from 0 to 1 (0 when the button is all the way at the bottom)
86+ */
87+ private fun coefficient (childDefaultYPosition : Int , dependencyYPosition : Int ): Float {
88+ val diff = childDefaultYPosition - dependencyYPosition
89+
90+ if (diff < 0 ) {
91+ return 1.0f
92+ }
93+ val coef = animationHeight - diff
94+ return if (coef < 0.0 ) {
95+ 0.0f
96+ } else coef / animationHeight.toFloat()
97+ }
98+ }
0 commit comments