Browse Source

Fling collapsing react view screens (#1213)

Guy Carmeli 7 years ago
parent
commit
a2a9324b1e

+ 2
- 2
android/app/src/main/java/com/reactnativenavigation/screens/CollapsingSingleScreen.java View File

81
                     @Override
81
                     @Override
82
                     public void onFling(CollapseAmount amount) {
82
                     public void onFling(CollapseAmount amount) {
83
                         if (screenParams.styleParams.drawScreenBelowTopBar) {
83
                         if (screenParams.styleParams.drawScreenBelowTopBar) {
84
-                            ((CollapsingView) contentView).collapse(amount);
84
+                            ((CollapsingView) contentView).fling(amount);
85
                         }
85
                         }
86
-                        topBar.collapse(amount);
86
+                        topBar.fling(amount);
87
                     }
87
                     }
88
                 },
88
                 },
89
                 getCollapseBehaviour()
89
                 getCollapseBehaviour()

+ 2
- 2
android/app/src/main/java/com/reactnativenavigation/screens/CollapsingViewPagerScreen.java View File

80
 
80
 
81
                     @Override
81
                     @Override
82
                     public void onFling(CollapseAmount amount) {
82
                     public void onFling(CollapseAmount amount) {
83
-                        topBar.collapse(amount);
84
-                        ((CollapsingView) viewPager).collapse(amount);
83
+                        topBar.fling(amount);
84
+                        ((CollapsingView) viewPager).fling(amount);
85
                     }
85
                     }
86
                 },
86
                 },
87
                 getCollapseBehaviour()
87
                 getCollapseBehaviour()

+ 5
- 0
android/app/src/main/java/com/reactnativenavigation/views/CollapsingContentView.java View File

57
         viewCollapser.collapse(amount);
57
         viewCollapser.collapse(amount);
58
     }
58
     }
59
 
59
 
60
+    @Override
61
+    public void fling(CollapseAmount amount) {
62
+        viewCollapser.fling(amount);
63
+    }
64
+
60
     public void destroy() {
65
     public void destroy() {
61
         if (scrollViewDetector != null) {
66
         if (scrollViewDetector != null) {
62
             scrollViewDetector.destroy();
67
             scrollViewDetector.destroy();

+ 2
- 2
android/app/src/main/java/com/reactnativenavigation/views/collapsingToolbar/CollapseAmount.java View File

24
         return amount != null;
24
         return amount != null;
25
     }
25
     }
26
 
26
 
27
-    public boolean collapseToTop() {
27
+    boolean collapseToTop() {
28
         return direction == CollapseCalculator.Direction.Up;
28
         return direction == CollapseCalculator.Direction.Up;
29
     }
29
     }
30
 
30
 
31
-    public boolean collapseToBottom() {
31
+    boolean collapseToBottom() {
32
         return direction == CollapseCalculator.Direction.Down;
32
         return direction == CollapseCalculator.Direction.Down;
33
     }
33
     }
34
 
34
 

+ 29
- 8
android/app/src/main/java/com/reactnativenavigation/views/collapsingToolbar/CollapseCalculator.java View File

2
 
2
 
3
 import android.support.annotation.NonNull;
3
 import android.support.annotation.NonNull;
4
 import android.support.annotation.Nullable;
4
 import android.support.annotation.Nullable;
5
+import android.util.Log;
5
 import android.view.GestureDetector;
6
 import android.view.GestureDetector;
6
 import android.view.MotionEvent;
7
 import android.view.MotionEvent;
7
 import android.view.ViewConfiguration;
8
 import android.view.ViewConfiguration;
12
 import com.reactnativenavigation.views.collapsingToolbar.behaviours.TitleBarHideOnScrollBehaviour;
13
 import com.reactnativenavigation.views.collapsingToolbar.behaviours.TitleBarHideOnScrollBehaviour;
13
 
14
 
14
 public class CollapseCalculator {
15
 public class CollapseCalculator {
16
+    private static final int FLING_DISTANCE_PIXELS_THRESHOLD = 100;
17
+    private static final int FLING_VELOCITY = 7000;
18
+
15
     public enum Direction {
19
     public enum Direction {
16
         Up, Down, None
20
         Up, Down, None
17
     }
21
     }
32
     private int scrollY = 0;
36
     private int scrollY = 0;
33
     private float totalCollapse = 0;
37
     private float totalCollapse = 0;
34
     private float totalCollapseDeltaSinceTouchDown = 0;
38
     private float totalCollapseDeltaSinceTouchDown = 0;
35
-    private final int scaledTouchSlop;
39
+    private final ViewConfiguration viewConfiguration;
36
 
40
 
37
     public CollapseCalculator(final CollapsingView collapsingView, CollapseBehaviour collapseBehaviour) {
41
     public CollapseCalculator(final CollapsingView collapsingView, CollapseBehaviour collapseBehaviour) {
38
         this.view = collapsingView;
42
         this.view = collapsingView;
39
         this.collapseBehaviour = collapseBehaviour;
43
         this.collapseBehaviour = collapseBehaviour;
40
-        scaledTouchSlop = ViewConfiguration.get(NavigationApplication.instance).getScaledTouchSlop();
44
+        viewConfiguration = ViewConfiguration.get(NavigationApplication.instance);
41
         setFlingDetector();
45
         setFlingDetector();
42
     }
46
     }
43
 
47
 
46
             flingDetector =
50
             flingDetector =
47
                     new GestureDetector(NavigationApplication.instance, new GestureDetector.SimpleOnGestureListener() {
51
                     new GestureDetector(NavigationApplication.instance, new GestureDetector.SimpleOnGestureListener() {
48
                         @Override
52
                         @Override
49
-                        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
53
+                        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, final float velocityY) {
50
                             final Direction direction = getScrollDirection(e1, e2);
54
                             final Direction direction = getScrollDirection(e1, e2);
55
+                            final float diff = Math.abs(e2.getRawY() - e1.getRawY());
56
+                            if (Math.abs(velocityY) < viewConfiguration.getScaledMinimumFlingVelocity() ||
57
+                                diff < FLING_DISTANCE_PIXELS_THRESHOLD) {
58
+                                Log.w("FLING", "Consuming fling v: [" + velocityY + "] dy: [" + diff + "]");
59
+                                return false;
60
+                            }
61
+
51
                             if (canCollapse(direction) && totalCollapse != 0) {
62
                             if (canCollapse(direction) && totalCollapse != 0) {
52
                                 flingListener.onFling(new CollapseAmount(direction));
63
                                 flingListener.onFling(new CollapseAmount(direction));
64
+                                if (direction == Direction.Up) {
65
+                                    scrollView.postDelayed(new Runnable() {
66
+                                        @Override
67
+                                        public void run() {
68
+                                            int max = (int) Math.max(Math.abs(velocityY), FLING_VELOCITY);
69
+                                            scrollView.fling(max);
70
+                                        }
71
+                                    }, ViewCollapser.FLING_DURATION);
72
+                                }
73
+
53
                                 return true;
74
                                 return true;
54
                             }
75
                             }
55
                             return false;
76
                             return false;
76
     @NonNull
97
     @NonNull
77
     CollapseAmount calculate(MotionEvent event) {
98
     CollapseAmount calculate(MotionEvent event) {
78
         updateInitialTouchY(event);
99
         updateInitialTouchY(event);
79
-        CollapseAmount touchUpCollapse = shouldCollapseOnTouchUp(event);
100
+        final boolean isFling = flingDetector.onTouchEvent(event);
101
+        CollapseAmount touchUpCollapse = shouldCollapseOnTouchUp(event, isFling);
80
         if (touchUpCollapse != CollapseAmount.None) {
102
         if (touchUpCollapse != CollapseAmount.None) {
81
             previousTouchEvent = MotionEvent.obtain(event);
103
             previousTouchEvent = MotionEvent.obtain(event);
82
             return touchUpCollapse;
104
             return touchUpCollapse;
97
         }
119
         }
98
     }
120
     }
99
 
121
 
100
-    private CollapseAmount shouldCollapseOnTouchUp(MotionEvent event) {
101
-        if ((collapseBehaviour.shouldCollapseOnTouchUp())
102
-            && !flingDetector.onTouchEvent(event) && isTouchUp(event)) {
122
+    private CollapseAmount shouldCollapseOnTouchUp(MotionEvent event, boolean isFling) {
123
+        if (isTouchUp(event) && collapseBehaviour.shouldCollapseOnTouchUp() && !isFling) {
103
             final float visibilityPercentage = view.getCurrentCollapseValue() / view.getFinalCollapseValue();
124
             final float visibilityPercentage = view.getCurrentCollapseValue() / view.getFinalCollapseValue();
104
             Direction direction = visibilityPercentage >= 0.5f ? Direction.Up : Direction.Down;
125
             Direction direction = visibilityPercentage >= 0.5f ? Direction.Up : Direction.Down;
105
             if (canCollapse(direction) && totalCollapse != 0) {
126
             if (canCollapse(direction) && totalCollapse != 0) {
195
         totalCollapseDeltaSinceTouchDown += Math.abs(y - previousCollapseY);
216
         totalCollapseDeltaSinceTouchDown += Math.abs(y - previousCollapseY);
196
         previousCollapseY = y;
217
         previousCollapseY = y;
197
         previousTouchEvent = MotionEvent.obtain(event);
218
         previousTouchEvent = MotionEvent.obtain(event);
198
-        return totalCollapseDeltaSinceTouchDown < scaledTouchSlop ? CollapseAmount.None : new CollapseAmount(collapse);
219
+        return totalCollapseDeltaSinceTouchDown < viewConfiguration.getScaledTouchSlop() ? CollapseAmount.None : new CollapseAmount(collapse);
199
     }
220
     }
200
 
221
 
201
     private float calculateCollapse(float y) {
222
     private float calculateCollapse(float y) {

+ 5
- 0
android/app/src/main/java/com/reactnativenavigation/views/collapsingToolbar/CollapsingTopBar.java View File

138
         }
138
         }
139
     }
139
     }
140
 
140
 
141
+    @Override
142
+    public void fling(CollapseAmount amount) {
143
+        viewCollapser.fling(amount, (CollapsingTitleBar) titleBar, header);
144
+    }
145
+
141
     public void onScrollViewAdded(ScrollView scrollView) {
146
     public void onScrollViewAdded(ScrollView scrollView) {
142
         scrollListener.onScrollViewAdded(scrollView);
147
         scrollListener.onScrollViewAdded(scrollView);
143
     }
148
     }

+ 1
- 0
android/app/src/main/java/com/reactnativenavigation/views/collapsingToolbar/CollapsingTopBarReactHeader.java View File

115
     }
115
     }
116
 
116
 
117
     private void onTouchDown(MotionEvent ev) {
117
     private void onTouchDown(MotionEvent ev) {
118
+        listener.onTouch(ev);
118
         if (touchDown == -1) {
119
         if (touchDown == -1) {
119
             touchDown = (int) ev.getRawY();
120
             touchDown = (int) ev.getRawY();
120
         }
121
         }

+ 2
- 0
android/app/src/main/java/com/reactnativenavigation/views/collapsingToolbar/CollapsingView.java View File

10
     View asView();
10
     View asView();
11
 
11
 
12
     void collapse(CollapseAmount amount);
12
     void collapse(CollapseAmount amount);
13
+
14
+    void fling(CollapseAmount amount);
13
 }
15
 }

+ 5
- 0
android/app/src/main/java/com/reactnativenavigation/views/collapsingToolbar/CollapsingViewPager.java View File

43
     public void collapse(CollapseAmount amount) {
43
     public void collapse(CollapseAmount amount) {
44
         viewCollapser.collapse(amount);
44
         viewCollapser.collapse(amount);
45
     }
45
     }
46
+
47
+    @Override
48
+    public void fling(CollapseAmount amount) {
49
+        viewCollapser.fling(amount);
50
+    }
46
 }
51
 }

+ 40
- 0
android/app/src/main/java/com/reactnativenavigation/views/collapsingToolbar/ViewCollapser.java View File

2
 
2
 
3
 import android.animation.Animator;
3
 import android.animation.Animator;
4
 import android.animation.AnimatorListenerAdapter;
4
 import android.animation.AnimatorListenerAdapter;
5
+import android.animation.ObjectAnimator;
6
+import android.animation.ValueAnimator;
7
+import android.support.annotation.NonNull;
8
+import android.view.View;
5
 import android.view.ViewPropertyAnimator;
9
 import android.view.ViewPropertyAnimator;
10
+import android.view.animation.DecelerateInterpolator;
6
 
11
 
7
 public class ViewCollapser {
12
 public class ViewCollapser {
8
     private static final int DURATION = 160;
13
     private static final int DURATION = 160;
14
+    public static final int FLING_DURATION = 40;
9
     private CollapsingView view;
15
     private CollapsingView view;
10
     private ViewPropertyAnimator animator;
16
     private ViewPropertyAnimator animator;
17
+    private final ValueAnimator.AnimatorUpdateListener LISTENER =
18
+            new ValueAnimator.AnimatorUpdateListener() {
19
+                @Override
20
+                public void onAnimationUpdate(ValueAnimator animation) {
21
+                }
22
+            };
11
 
23
 
12
     public ViewCollapser(CollapsingView view) {
24
     public ViewCollapser(CollapsingView view) {
13
         this.view = view;
25
         this.view = view;
55
                 });
67
                 });
56
         animator.start();
68
         animator.start();
57
     }
69
     }
70
+
71
+    void fling(final CollapseAmount amount, final CollapsingTitleBar titleBar, final CollapsingTopBarReactHeader header) {
72
+        fling(amount, new ValueAnimator.AnimatorUpdateListener() {
73
+            @Override
74
+            public void onAnimationUpdate(ValueAnimator animation) {
75
+                titleBar.collapse(new CollapseAmount((Float) animation.getAnimatedValue()));
76
+                header.collapse((Float) animation.getAnimatedValue());
77
+            }
78
+        });
79
+    }
80
+
81
+    public void fling(CollapseAmount amount) {
82
+        fling(amount, LISTENER);
83
+    }
84
+
85
+    private void fling(final CollapseAmount amount, @NonNull final ValueAnimator.AnimatorUpdateListener updateListener) {
86
+        final float translation = amount.collapseToTop() ? view.getFinalCollapseValue() : 0;
87
+        ObjectAnimator animator = ObjectAnimator.ofFloat(view.asView(), View.TRANSLATION_Y, translation);
88
+        animator.setDuration(FLING_DURATION);
89
+        animator.setInterpolator(new DecelerateInterpolator());
90
+        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
91
+            @Override
92
+            public void onAnimationUpdate(ValueAnimator animation) {
93
+                updateListener.onAnimationUpdate(animation);
94
+            }
95
+        });
96
+        animator.start();
97
+    }
58
 }
98
 }

+ 1
- 1
android/app/src/main/java/com/reactnativenavigation/views/collapsingToolbar/behaviours/CollapseTopBarBehaviour.java View File

3
 public class CollapseTopBarBehaviour implements CollapseBehaviour {
3
 public class CollapseTopBarBehaviour implements CollapseBehaviour {
4
     @Override
4
     @Override
5
     public boolean shouldCollapseOnFling() {
5
     public boolean shouldCollapseOnFling() {
6
-        return false;
6
+        return true;
7
     }
7
     }
8
 
8
 
9
     @Override
9
     @Override