1+ package com .akiniyalocts .minor .behavior ;
2+
3+ import android .animation .ObjectAnimator ;
4+ import android .animation .ValueAnimator ;
5+ import android .content .Context ;
6+ import android .os .Build ;
7+ import android .support .design .widget .CoordinatorLayout ;
8+ import android .support .design .widget .FloatingActionButton ;
9+ import android .support .design .widget .Snackbar ;
10+ import android .support .v4 .view .ViewCompat ;
11+ import android .support .v4 .view .ViewPropertyAnimatorCompat ;
12+ import android .support .v4 .view .ViewPropertyAnimatorUpdateListener ;
13+ import android .support .v4 .view .animation .LinearOutSlowInInterpolator ;
14+ import android .util .AttributeSet ;
15+ import android .view .View ;
16+ import android .view .ViewGroup ;
17+ import android .view .animation .Interpolator ;
18+
19+ /**
20+ * Repurposed from: https://github.com/aurelhubert/ahbottomnavigation
21+ */
22+ public class MinorBehavior <V extends View > extends VerticalScrollingBehavior <V > {
23+
24+ private static final Interpolator INTERPOLATOR = new LinearOutSlowInInterpolator ();
25+ private static final int ANIM_DURATION = 300 ;
26+
27+ private boolean hidden = false ;
28+ private ViewPropertyAnimatorCompat translationAnimator ;
29+ private ObjectAnimator translationObjectAnimator ;
30+ private Snackbar .SnackbarLayout snackbarLayout ;
31+ private FloatingActionButton floatingActionButton ;
32+ private int mSnackbarHeight = -1 ;
33+ private boolean fabBottomMarginInitialized = false ;
34+ private float targetOffset = 0 , fabTargetOffset = 0 , fabDefaultBottomMargin = 0 , snackBarY = 0 ;
35+ private boolean behaviorTranslationEnabled = true ;
36+
37+ public MinorBehavior () {
38+ super ();
39+ }
40+
41+ public MinorBehavior (boolean behaviorTranslationEnabled ) {
42+ super ();
43+ this .behaviorTranslationEnabled = behaviorTranslationEnabled ;
44+ }
45+
46+ public MinorBehavior (Context context , AttributeSet attrs ) {
47+ super (context , attrs );
48+ }
49+
50+ @ Override
51+ public boolean onLayoutChild (CoordinatorLayout parent , V child , int layoutDirection ) {
52+ boolean layoutChild = super .onLayoutChild (parent , child , layoutDirection );
53+ return layoutChild ;
54+ }
55+
56+ @ Override
57+ public boolean onDependentViewChanged (CoordinatorLayout parent , V child , View dependency ) {
58+ return super .onDependentViewChanged (parent , child , dependency );
59+ }
60+
61+ @ Override
62+ public void onDependentViewRemoved (CoordinatorLayout parent , V child , View dependency ) {
63+ super .onDependentViewRemoved (parent , child , dependency );
64+ }
65+
66+ @ Override
67+ public boolean layoutDependsOn (CoordinatorLayout parent , V child , View dependency ) {
68+ updateSnackbar (child , dependency );
69+ updateFloatingActionButton (dependency );
70+ return super .layoutDependsOn (parent , child , dependency );
71+ }
72+
73+ @ Override
74+ public void onNestedVerticalOverScroll (CoordinatorLayout coordinatorLayout , V child , @ ScrollDirection int direction , int currentOverScroll , int totalOverScroll ) {
75+ }
76+
77+ @ Override
78+ public void onDirectionNestedPreScroll (CoordinatorLayout coordinatorLayout , V child , View target , int dx , int dy , int [] consumed , @ ScrollDirection int scrollDirection ) {
79+ handleDirection (child , scrollDirection );
80+ }
81+
82+
83+ private void handleDirection (V child , int scrollDirection ) {
84+ if (scrollDirection == ScrollDirection .SCROLL_DIRECTION_DOWN && hidden ) {
85+ hidden = false ;
86+ animateOffset (child , 0 , false , true );
87+ } else if (scrollDirection == ScrollDirection .SCROLL_DIRECTION_UP && !hidden ) {
88+ hidden = true ;
89+ animateOffset (child , child .getHeight (), false , true );
90+ }
91+ }
92+
93+ @ Override
94+ protected boolean onNestedDirectionFling (CoordinatorLayout coordinatorLayout , V child , View target , float velocityX , float velocityY , @ ScrollDirection int scrollDirection ) {
95+ handleDirection (child , scrollDirection );
96+ return true ;
97+ }
98+
99+ /**
100+ * Animate offset
101+ *
102+ * @param child
103+ * @param offset
104+ */
105+ private void animateOffset (final V child , final int offset , boolean forceAnimation , boolean withAnimation ) {
106+ if (!behaviorTranslationEnabled && !forceAnimation ) {
107+ return ;
108+ }
109+
110+ if (Build .VERSION .SDK_INT < Build .VERSION_CODES .KITKAT ) {
111+ ensureOrCancelObjectAnimation (child , offset , withAnimation );
112+ translationObjectAnimator .start ();
113+ } else {
114+ ensureOrCancelAnimator (child , withAnimation );
115+ translationAnimator .translationY (offset ).start ();
116+ }
117+ }
118+
119+ /**
120+ * Manage animation for Android >= KITKAT
121+ *
122+ * @param child
123+ */
124+ private void ensureOrCancelAnimator (V child , boolean withAnimation ) {
125+ if (translationAnimator == null ) {
126+ translationAnimator = ViewCompat .animate (child );
127+ translationAnimator .setDuration (withAnimation ? ANIM_DURATION : 0 );
128+ translationAnimator .setUpdateListener (new ViewPropertyAnimatorUpdateListener () {
129+ @ Override
130+ public void onAnimationUpdate (View view ) {
131+ // Animate snackbar
132+ if (snackbarLayout != null && snackbarLayout .getLayoutParams () instanceof ViewGroup .MarginLayoutParams ) {
133+ targetOffset = view .getMeasuredHeight () - view .getTranslationY ();
134+ ViewGroup .MarginLayoutParams p = (ViewGroup .MarginLayoutParams ) snackbarLayout .getLayoutParams ();
135+ p .setMargins (p .leftMargin , p .topMargin , p .rightMargin , (int ) targetOffset );
136+ snackbarLayout .requestLayout ();
137+ }
138+ // Animate Floating Action Button
139+ if (floatingActionButton != null && floatingActionButton .getLayoutParams () instanceof ViewGroup .MarginLayoutParams ) {
140+ ViewGroup .MarginLayoutParams p = (ViewGroup .MarginLayoutParams ) floatingActionButton .getLayoutParams ();
141+ fabTargetOffset = fabDefaultBottomMargin - view .getTranslationY () + snackBarY ;
142+ p .setMargins (p .leftMargin , p .topMargin , p .rightMargin , (int ) fabTargetOffset );
143+ floatingActionButton .requestLayout ();
144+ }
145+ }
146+ });
147+ translationAnimator .setInterpolator (INTERPOLATOR );
148+ } else {
149+ translationAnimator .setDuration (withAnimation ? ANIM_DURATION : 0 );
150+ translationAnimator .cancel ();
151+ }
152+ }
153+
154+ /**
155+ * Manage animation for Android < KITKAT
156+ *
157+ * @param child
158+ */
159+ private void ensureOrCancelObjectAnimation (final V child , final int offset , boolean withAnimation ) {
160+
161+ if (translationObjectAnimator != null ) {
162+ translationObjectAnimator .cancel ();
163+ }
164+
165+ translationObjectAnimator = ObjectAnimator .ofFloat (child , View .TRANSLATION_Y , offset );
166+ translationObjectAnimator .setDuration (withAnimation ? ANIM_DURATION : 0 );
167+ translationObjectAnimator .setInterpolator (INTERPOLATOR );
168+ translationObjectAnimator .addUpdateListener (new ValueAnimator .AnimatorUpdateListener () {
169+ @ Override
170+ public void onAnimationUpdate (ValueAnimator animation ) {
171+ if (snackbarLayout != null && snackbarLayout .getLayoutParams () instanceof ViewGroup .MarginLayoutParams ) {
172+ targetOffset = child .getMeasuredHeight () - child .getTranslationY ();
173+ ViewGroup .MarginLayoutParams p = (ViewGroup .MarginLayoutParams ) snackbarLayout .getLayoutParams ();
174+ p .setMargins (p .leftMargin , p .topMargin , p .rightMargin , (int ) targetOffset );
175+ snackbarLayout .requestLayout ();
176+ }
177+ // Animate Floating Action Button
178+ if (floatingActionButton != null && floatingActionButton .getLayoutParams () instanceof ViewGroup .MarginLayoutParams ) {
179+ ViewGroup .MarginLayoutParams p = (ViewGroup .MarginLayoutParams ) floatingActionButton .getLayoutParams ();
180+ fabTargetOffset = fabDefaultBottomMargin - child .getTranslationY () + snackBarY ;
181+ p .setMargins (p .leftMargin , p .topMargin , p .rightMargin , (int ) fabTargetOffset );
182+ floatingActionButton .requestLayout ();
183+ }
184+ }
185+ });
186+ }
187+
188+
189+ public static <V extends View > MinorBehavior <V > from (V view ) {
190+ ViewGroup .LayoutParams params = view .getLayoutParams ();
191+ if (!(params instanceof CoordinatorLayout .LayoutParams )) {
192+ throw new IllegalArgumentException ("The view is not a child of CoordinatorLayout" );
193+ }
194+ CoordinatorLayout .Behavior behavior = ((CoordinatorLayout .LayoutParams ) params )
195+ .getBehavior ();
196+ if (!(behavior instanceof MinorBehavior )) {
197+ throw new IllegalArgumentException (
198+ "The view is not associated with MinorBehavior" );
199+ }
200+ return (MinorBehavior <V >) behavior ;
201+ }
202+
203+
204+ /**
205+ * Update Snackbar bottom margin
206+ */
207+ public void updateSnackbar (final View child , View dependency ) {
208+
209+ if (dependency != null && dependency instanceof Snackbar .SnackbarLayout ) {
210+
211+ snackbarLayout = (Snackbar .SnackbarLayout ) dependency ;
212+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .KITKAT ) {
213+ snackbarLayout .addOnLayoutChangeListener (new View .OnLayoutChangeListener () {
214+ @ Override
215+ public void onLayoutChange (View v , int left , int top , int right , int bottom ,
216+ int oldLeft , int oldTop , int oldRight , int oldBottom ) {
217+ if (floatingActionButton != null &&
218+ floatingActionButton .getLayoutParams () instanceof ViewGroup .MarginLayoutParams ) {
219+ ViewGroup .MarginLayoutParams p = (ViewGroup .MarginLayoutParams ) floatingActionButton .getLayoutParams ();
220+ snackBarY = bottom - v .getY ();
221+ fabTargetOffset = fabDefaultBottomMargin - child .getTranslationY () + snackBarY ;
222+ p .setMargins (p .leftMargin , p .topMargin , p .rightMargin , (int ) fabTargetOffset );
223+ floatingActionButton .requestLayout ();
224+ }
225+ }
226+ });
227+ }
228+
229+ if (mSnackbarHeight == -1 ) {
230+ mSnackbarHeight = dependency .getHeight ();
231+ }
232+
233+ int targetMargin = (int ) (child .getMeasuredHeight () - child .getTranslationY ());
234+ if (Build .VERSION .SDK_INT < Build .VERSION_CODES .LOLLIPOP ) {
235+ child .bringToFront ();
236+ }
237+
238+ if (dependency .getLayoutParams () instanceof ViewGroup .MarginLayoutParams ) {
239+ ViewGroup .MarginLayoutParams p = (ViewGroup .MarginLayoutParams ) dependency .getLayoutParams ();
240+ p .setMargins (p .leftMargin , p .topMargin , p .rightMargin , targetMargin );
241+ dependency .requestLayout ();
242+ }
243+ }
244+ }
245+
246+ /**
247+ * Update floating action button bottom margin
248+ */
249+ public void updateFloatingActionButton (View dependency ) {
250+ if (dependency != null && dependency instanceof FloatingActionButton ) {
251+ floatingActionButton = (FloatingActionButton ) dependency ;
252+ if (!fabBottomMarginInitialized && dependency .getLayoutParams () instanceof ViewGroup .MarginLayoutParams ) {
253+ fabBottomMarginInitialized = true ;
254+ ViewGroup .MarginLayoutParams p = (ViewGroup .MarginLayoutParams ) dependency .getLayoutParams ();
255+ fabDefaultBottomMargin = p .bottomMargin ;
256+ }
257+ }
258+ }
259+ }
0 commit comments