1+ package com .zulip .android .util ;
2+
3+ import android .animation .Animator ;
4+ import android .annotation .SuppressLint ;
5+ import android .content .Context ;
6+ import android .support .design .widget .AppBarLayout ;
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 .animation .FastOutSlowInInterpolator ;
12+ import android .support .v7 .widget .LinearLayoutManager ;
13+ import android .util .AttributeSet ;
14+ import android .util .TypedValue ;
15+ import android .view .View ;
16+ import android .view .ViewPropertyAnimator ;
17+ import android .view .animation .Interpolator ;
18+ import android .widget .LinearLayout ;
19+
20+ import com .zulip .android .R ;
21+ import com .zulip .android .activities .RecyclerMessageAdapter ;
22+
23+ import java .util .List ;
24+
25+ /**
26+ * This hides the {@link android.support.design.widget.FloatingActionButton} when the
27+ * recyclerView is scrolled, used in here {@link com.zulip.android.R.layout#main} as a behaviour.
28+ * This also shrinks the {@link android.support.design.widget.FloatingActionButton} when the snackbar comes
29+ * and goes in and out of view.
30+ */
31+
32+
33+ public class RemoveFabOnScroll extends CoordinatorLayout .Behavior <FloatingActionButton > {
34+
35+ private static final Interpolator FAST_OUT_SLOW_IN_INTERPOLATOR = new FastOutSlowInInterpolator ();
36+ private static float toolbarHeight ;
37+ private int changeInYDir ;
38+ private boolean mIsShowing ;
39+ private boolean isViewHidden ;
40+ private View chatBox ;
41+ private LinearLayoutManager linearLayoutManager ;
42+ private RecyclerMessageAdapter adapter ;
43+
44+ public RemoveFabOnScroll (Context context , AttributeSet attrs ) {
45+ super (context , attrs );
46+ TypedValue tv = new TypedValue ();
47+ if (context .getTheme ().resolveAttribute (android .R .attr .actionBarSize , tv , true ))
48+ toolbarHeight = TypedValue .complexToDimensionPixelSize (tv .data , context .getResources ().getDisplayMetrics ());
49+ }
50+
51+ public RemoveFabOnScroll (LinearLayoutManager linearLayoutManager , RecyclerMessageAdapter adapter ) {
52+ this .linearLayoutManager = linearLayoutManager ;
53+ this .adapter = adapter ;
54+ }
55+
56+ @ Override
57+ public boolean onStartNestedScroll (CoordinatorLayout coordinatorLayout , FloatingActionButton child , View directTargetChild , View target , int nestedScrollAxes ) {
58+ return (nestedScrollAxes & ViewCompat .SCROLL_AXIS_VERTICAL ) != 0 ;
59+ }
60+
61+ @ SuppressLint ("NewApi" )
62+ @ Override
63+ public void onNestedPreScroll (CoordinatorLayout coordinatorLayout , FloatingActionButton child , View target , int dx , int dy , int [] consumed ) throws NullPointerException {
64+ //count index starts from 1 where as position starts from 0, thus difference 1
65+ //we have 2 loading layouts one at top and another at bottom of the messages which should be ignored
66+ //resulting in a overall difference of 3
67+ if (linearLayoutManager .findLastCompletelyVisibleItemPosition () < adapter .getItemCount () - 3 ) {
68+ if (dy > 0 && changeInYDir < 0 || dy < 0 && changeInYDir > 0 ) {
69+ child .animate ().cancel ();
70+ changeInYDir = 0 ;
71+ }
72+
73+ changeInYDir += dy ;
74+ if ((changeInYDir > toolbarHeight && child .getVisibility () == View .VISIBLE ) && (!isViewHidden || isTopSnackBar (child )))
75+ hideView (child );
76+ else if (changeInYDir < 0 && (child .getVisibility () == View .GONE && !mIsShowing ) || isTopSnackBar (child )) {
77+ if (chatBox == null )
78+ chatBox = coordinatorLayout .findViewById (R .id .messageBoxContainer );
79+ if (chatBox .getVisibility () == View .VISIBLE ) {
80+ return ;
81+ }
82+ showView (child );
83+ }
84+ }
85+ }
86+
87+ private boolean isTopSnackBar (View child ) {
88+ return (child .getId () != R .id .appBarLayout && !(child instanceof FloatingActionButton ));
89+ }
90+
91+ @ SuppressLint ("NewApi" )
92+ private void hideView (final View view ) {
93+ isViewHidden = true ;
94+ int y = view .getHeight ();
95+ ;
96+ if (view instanceof AppBarLayout ) {
97+ y = -1 * view .getHeight ();
98+ } else if (view instanceof LinearLayout ) {
99+ y = 0 ;
100+ }
101+ ViewPropertyAnimator animator = view .animate ()
102+ .translationY (y )
103+ .setInterpolator (FAST_OUT_SLOW_IN_INTERPOLATOR )
104+ .setDuration (200 );
105+
106+ animator .setListener (new Animator .AnimatorListener () {
107+ @ Override
108+ public void onAnimationStart (Animator animator ) {
109+ }
110+
111+ @ Override
112+ public void onAnimationEnd (Animator animator ) {
113+ isViewHidden = false ;
114+ view .setVisibility (View .GONE );
115+ }
116+
117+ @ Override
118+ public void onAnimationCancel (Animator animator ) {
119+ isViewHidden = false ;
120+ if (!mIsShowing )
121+ showView (view );
122+ }
123+
124+ @ Override
125+ public void onAnimationRepeat (Animator animator ) {
126+ }
127+ });
128+ animator .start ();
129+ }
130+
131+ @ SuppressLint ("NewApi" )
132+ public void showView (final View view ) {
133+ mIsShowing = true ;
134+ ViewPropertyAnimator animator = view .animate ()
135+ .translationY ((view .getId () == R .id .appBarLayout || view instanceof FloatingActionButton ) ? 0 : toolbarHeight )
136+ .setInterpolator (FAST_OUT_SLOW_IN_INTERPOLATOR )
137+ .setDuration (200 );
138+
139+ animator .setListener (new Animator .AnimatorListener () {
140+ @ Override
141+ public void onAnimationStart (Animator animator ) {
142+ view .setVisibility (View .VISIBLE );
143+ }
144+
145+ @ Override
146+ public void onAnimationEnd (Animator animator ) {
147+ mIsShowing = false ;
148+ }
149+
150+ @ Override
151+ public void onAnimationCancel (Animator animator ) {
152+ mIsShowing = false ;
153+ if (!isViewHidden )
154+ hideView (view );
155+ }
156+
157+ @ Override
158+ public void onAnimationRepeat (Animator animator ) {
159+ }
160+ });
161+ animator .start ();
162+ }
163+
164+ @ Override
165+ public boolean layoutDependsOn (CoordinatorLayout parent , FloatingActionButton child , View dependency ) {
166+ return dependency instanceof Snackbar .SnackbarLayout ;
167+ }
168+
169+ @ Override
170+ public boolean onDependentViewChanged (CoordinatorLayout parent , FloatingActionButton child , View dependency ) {
171+ float translationY = getFabTranslationYForSnackbar (parent , child );
172+ float percentComplete = -translationY / dependency .getHeight ();
173+ float scaleFactor = 1 - percentComplete ;
174+
175+ child .setScaleX (scaleFactor );
176+ child .setScaleY (scaleFactor );
177+ return false ;
178+ }
179+
180+ private float getFabTranslationYForSnackbar (CoordinatorLayout parent ,
181+ FloatingActionButton fab ) {
182+ float minOffset = 0 ;
183+ final List <View > dependencies = parent .getDependencies (fab );
184+ for (int i = 0 , z = dependencies .size (); i < z ; i ++) {
185+ final View view = dependencies .get (i );
186+ if (view instanceof Snackbar .SnackbarLayout && parent .doViewsOverlap (fab , view )) {
187+ minOffset = Math .min (minOffset ,
188+ ViewCompat .getTranslationY (view ) - view .getHeight ());
189+ }
190+ }
191+
192+ return minOffset ;
193+ }
194+
195+ }
0 commit comments