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