Browse Source

Add push and pop screen transition

This commit is the first step in adding proper screen transition
animations to push and pop actions.
Guy Carmeli 8 years ago
parent
commit
187144675e

+ 14
- 7
android/app/src/main/java/com/reactnativenavigation/layouts/BottomTabsLayout.java View File

@@ -9,6 +9,7 @@ import com.reactnativenavigation.params.ActivityParams;
9 9
 import com.reactnativenavigation.params.ScreenParams;
10 10
 import com.reactnativenavigation.params.TitleBarButtonParams;
11 11
 import com.reactnativenavigation.params.TitleBarLeftButtonParams;
12
+import com.reactnativenavigation.screens.ScreenAnimator;
12 13
 import com.reactnativenavigation.screens.ScreenStack;
13 14
 import com.reactnativenavigation.views.BottomTabs;
14 15
 
@@ -24,6 +25,7 @@ public class BottomTabsLayout extends RelativeLayout implements Layout, AHBottom
24 25
     private BottomTabs bottomTabs;
25 26
     private ScreenStack[] screenStacks;
26 27
     private int currentStackIndex = 0;
28
+    private ScreenAnimator screenAnimator;
27 29
 
28 30
     public BottomTabsLayout(AppCompatActivity activity, ActivityParams params) {
29 31
         super(activity);
@@ -35,6 +37,7 @@ public class BottomTabsLayout extends RelativeLayout implements Layout, AHBottom
35 37
 
36 38
     private void createLayout() {
37 39
         createBottomTabs();
40
+        createScreenAnimator();
38 41
         addBottomTabsToScreen();
39 42
         addScreenStacks();
40 43
         showInitialScreenStack();
@@ -47,7 +50,7 @@ public class BottomTabsLayout extends RelativeLayout implements Layout, AHBottom
47 50
     }
48 51
 
49 52
     private void createAndAddScreenStack(int position) {
50
-        ScreenStack newStack = new ScreenStack(activity, params.tabParams.get(position), this);
53
+        ScreenStack newStack = new ScreenStack(activity, params.tabParams.get(position), this, screenAnimator);
51 54
         screenStacks[position] = newStack;
52 55
         newStack.setVisibility(INVISIBLE);
53 56
         addScreenStack(newStack);
@@ -70,6 +73,10 @@ public class BottomTabsLayout extends RelativeLayout implements Layout, AHBottom
70 73
         bottomTabs.addTabs(params.tabParams, this);
71 74
     }
72 75
 
76
+    private void createScreenAnimator() {
77
+        screenAnimator = new ScreenAnimator(bottomTabs);
78
+    }
79
+
73 80
     private void addBottomTabsToScreen() {
74 81
         LayoutParams lp = new LayoutParams(MATCH_PARENT, WRAP_CONTENT);
75 82
         lp.addRule(ALIGN_PARENT_BOTTOM);
@@ -88,7 +95,7 @@ public class BottomTabsLayout extends RelativeLayout implements Layout, AHBottom
88 95
     @Override
89 96
     public boolean onBackPressed() {
90 97
         if (getCurrentScreenStack().canPop()) {
91
-            getCurrentScreenStack().pop();
98
+            getCurrentScreenStack().pop(screenAnimator);
92 99
             setBottomTabsStyleFromCurrentScreen();
93 100
             return true;
94 101
         } else {
@@ -130,19 +137,19 @@ public class BottomTabsLayout extends RelativeLayout implements Layout, AHBottom
130 137
 
131 138
     @Override
132 139
     public void push(ScreenParams screenParams) {
133
-        getCurrentScreenStack().push(screenParams);
140
+        getCurrentScreenStack().push(screenAnimator, screenParams);
134 141
         bottomTabs.setStyleFromScreen(screenParams.styleParams);
135 142
     }
136 143
 
137 144
     @Override
138 145
     public void pop(ScreenParams screenParams) {
139
-        getCurrentScreenStack().pop();
146
+        getCurrentScreenStack().pop(screenAnimator);
140 147
         setBottomTabsStyleFromCurrentScreen();
141 148
     }
142 149
 
143 150
     @Override
144 151
     public void popToRoot(ScreenParams params) {
145
-        getCurrentScreenStack().popToRoot();
152
+        getCurrentScreenStack().popToRoot(screenAnimator);
146 153
         setBottomTabsStyleFromCurrentScreen();
147 154
     }
148 155
 
@@ -152,7 +159,7 @@ public class BottomTabsLayout extends RelativeLayout implements Layout, AHBottom
152 159
         currentScreenStack.destroy();
153 160
         removeView(currentScreenStack);
154 161
 
155
-        ScreenStack newStack = new ScreenStack(activity, params, this);
162
+        ScreenStack newStack = new ScreenStack(activity, params, this, screenAnimator);
156 163
         screenStacks[currentStackIndex] = newStack;
157 164
         addView(newStack, 0, new RelativeLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
158 165
 
@@ -198,7 +205,7 @@ public class BottomTabsLayout extends RelativeLayout implements Layout, AHBottom
198 205
     @Override
199 206
     public boolean onTitleBarBackPress() {
200 207
         if (getCurrentScreenStack().canPop()) {
201
-            getCurrentScreenStack().pop();
208
+            getCurrentScreenStack().pop(screenAnimator);
202 209
             setBottomTabsStyleFromCurrentScreen();
203 210
             return true;
204 211
         }

+ 12
- 5
android/app/src/main/java/com/reactnativenavigation/layouts/SingleScreenLayout.java View File

@@ -7,6 +7,7 @@ import android.widget.FrameLayout;
7 7
 import com.reactnativenavigation.params.ScreenParams;
8 8
 import com.reactnativenavigation.params.TitleBarButtonParams;
9 9
 import com.reactnativenavigation.params.TitleBarLeftButtonParams;
10
+import com.reactnativenavigation.screens.ScreenAnimator;
10 11
 import com.reactnativenavigation.screens.ScreenStack;
11 12
 import com.reactnativenavigation.views.TitleBarBackButtonListener;
12 13
 
@@ -19,6 +20,7 @@ public class SingleScreenLayout extends FrameLayout implements Layout {
19 20
     private final AppCompatActivity activity;
20 21
     private final ScreenParams screenParams;
21 22
     private ScreenStack stack;
23
+    private ScreenAnimator screenAnimator;
22 24
     private TitleBarBackButtonListener titleBarBackButtonListener;
23 25
 
24 26
     public SingleScreenLayout(AppCompatActivity activity, ScreenParams screenParams, TitleBarBackButtonListener titleBarBackButtonListener) {
@@ -30,22 +32,27 @@ public class SingleScreenLayout extends FrameLayout implements Layout {
30 32
         super(activity);
31 33
         this.activity = activity;
32 34
         this.screenParams = screenParams;
35
+        createScreenAnimator();
33 36
         createStack();
34 37
     }
35 38
 
39
+    private void createScreenAnimator() {
40
+        screenAnimator = new ScreenAnimator();
41
+    }
42
+
36 43
     private void createStack() {
37 44
         if (stack != null) {
38 45
             stack.destroy();
39 46
             removeView(stack);
40 47
         }
41
-        stack = new ScreenStack(activity, screenParams, this);
48
+        stack = new ScreenStack(activity, screenParams, this, screenAnimator);
42 49
         addView(stack, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
43 50
     }
44 51
 
45 52
     @Override
46 53
     public boolean onBackPressed() {
47 54
         if (stack.canPop()) {
48
-            stack.pop();
55
+            stack.pop(screenAnimator);
49 56
             return true;
50 57
         } else {
51 58
             return false;
@@ -60,17 +67,17 @@ public class SingleScreenLayout extends FrameLayout implements Layout {
60 67
 
61 68
     @Override
62 69
     public void push(ScreenParams params) {
63
-        stack.push(params);
70
+        stack.push(screenAnimator, params);
64 71
     }
65 72
 
66 73
     @Override
67 74
     public void pop(ScreenParams params) {
68
-        stack.pop();
75
+        stack.pop(screenAnimator);
69 76
     }
70 77
 
71 78
     @Override
72 79
     public void popToRoot(ScreenParams params) {
73
-        stack.popToRoot();
80
+        stack.popToRoot(screenAnimator);
74 81
     }
75 82
 
76 83
     @Override

+ 110
- 0
android/app/src/main/java/com/reactnativenavigation/screens/ScreenAnimator.java View File

@@ -0,0 +1,110 @@
1
+package com.reactnativenavigation.screens;
2
+
3
+import android.animation.Animator;
4
+import android.animation.AnimatorListenerAdapter;
5
+import android.animation.AnimatorSet;
6
+import android.animation.ObjectAnimator;
7
+import android.view.View;
8
+import android.view.animation.AccelerateInterpolator;
9
+import android.view.animation.DecelerateInterpolator;
10
+import android.view.animation.LinearInterpolator;
11
+
12
+import com.reactnativenavigation.utils.ViewUtils;
13
+import com.reactnativenavigation.views.BottomTabs;
14
+
15
+public class ScreenAnimator {
16
+    private static final String TAG = "ScreenAnimator";
17
+    private BottomTabs bottomTabs;
18
+
19
+    public ScreenAnimator() {
20
+
21
+    }
22
+
23
+    public ScreenAnimator(BottomTabs bottomTabs) {
24
+        this.bottomTabs = bottomTabs;
25
+    }
26
+
27
+    public void show(Screen screenToShow, Runnable onScreenRemoved) {
28
+        createPushAnimator(screenToShow, onScreenRemoved).start();
29
+    }
30
+
31
+    public void show(Screen screenToShow) {
32
+        createPushAnimator(screenToShow).start();
33
+    }
34
+
35
+    public void hide(Screen screenToHide, Runnable onScreenRemoved) {
36
+        createPopAnimator(screenToHide, onScreenRemoved).start();
37
+    }
38
+
39
+    private Animator createPushAnimator(final Screen screen) {
40
+        ObjectAnimator alpha = ObjectAnimator.ofFloat(screen, View.ALPHA, 0.7f, 1);
41
+        alpha.setStartDelay(100);
42
+        alpha.setInterpolator(new LinearInterpolator());
43
+        alpha.setDuration(150);
44
+
45
+        final float delta = 0.08f * ViewUtils.getScreenHeight();
46
+        ObjectAnimator translationY = ObjectAnimator.ofFloat(screen, View.TRANSLATION_Y, delta, 0);
47
+        translationY.setInterpolator(new AccelerateInterpolator());
48
+        translationY.setDuration(250);
49
+
50
+        AnimatorSet set = new AnimatorSet();
51
+        set.playTogether(translationY, alpha);
52
+        set.addListener(new AnimatorListenerAdapter() {
53
+            @Override
54
+            public void onAnimationStart(Animator animation) {
55
+                screen.setVisibility(View.VISIBLE);
56
+            }
57
+        });
58
+        return set;
59
+    }
60
+
61
+    private Animator createPushAnimator(final Screen screenToShow, final Runnable onScreenRemoved) {
62
+        final float translationYValue = 0.08f * ViewUtils.getScreenHeight();
63
+
64
+        ObjectAnimator alpha = ObjectAnimator.ofFloat(screenToShow, View.ALPHA, 0, 1);
65
+        alpha.setInterpolator(new DecelerateInterpolator());
66
+        alpha.setDuration(200);
67
+
68
+        ObjectAnimator translationY = ObjectAnimator.ofFloat(screenToShow, View.TRANSLATION_Y, translationYValue, 0);
69
+        translationY.setInterpolator(new DecelerateInterpolator());
70
+        translationY.setDuration(280);
71
+
72
+        AnimatorSet set = new AnimatorSet();
73
+        set.playTogether(translationY, alpha);
74
+        set.addListener(new AnimatorListenerAdapter() {
75
+            @Override
76
+            public void onAnimationStart(Animator animation) {
77
+                screenToShow.setVisibility(View.VISIBLE);
78
+            }
79
+
80
+            @Override
81
+            public void onAnimationEnd(Animator animation) {
82
+                onScreenRemoved.run();
83
+            }
84
+        });
85
+        return set;
86
+    }
87
+
88
+    private Animator createPopAnimator(final Screen screenToHide, final Runnable onScreenRemoved) {
89
+        final float translationYValue = 0.08f * ViewUtils.getScreenHeight();
90
+
91
+        ObjectAnimator alpha = ObjectAnimator.ofFloat(screenToHide, View.ALPHA, 0);
92
+        alpha.setInterpolator(new LinearInterpolator());
93
+        alpha.setStartDelay(100);
94
+        alpha.setDuration(150);
95
+
96
+        ObjectAnimator translationY = ObjectAnimator.ofFloat(screenToHide, View.TRANSLATION_Y, translationYValue);
97
+        translationY.setInterpolator(new AccelerateInterpolator());
98
+        translationY.setDuration(250);
99
+
100
+        AnimatorSet set = new AnimatorSet();
101
+        set.playTogether(translationY, alpha);
102
+        set.addListener(new AnimatorListenerAdapter() {
103
+            @Override
104
+            public void onAnimationEnd(Animator animation) {
105
+                onScreenRemoved.run();
106
+            }
107
+        });
108
+        return set;
109
+    }
110
+}

+ 34
- 20
android/app/src/main/java/com/reactnativenavigation/screens/ScreenStack.java View File

@@ -1,6 +1,5 @@
1 1
 package com.reactnativenavigation.screens;
2 2
 
3
-import android.animation.LayoutTransition;
4 3
 import android.support.v7.app.AppCompatActivity;
5 4
 import android.view.ViewManager;
6 5
 import android.widget.FrameLayout;
@@ -24,26 +23,37 @@ public class ScreenStack extends FrameLayout {
24 23
     private TitleBarBackButtonListener titleBarBackButtonListener;
25 24
     private Stack<Screen> stack = new Stack<>();
26 25
 
27
-    public ScreenStack(AppCompatActivity activity, ScreenParams initialScreenParams, TitleBarBackButtonListener titleBarBackButtonListener) {
26
+    public ScreenStack(AppCompatActivity activity,
27
+                       ScreenParams initialScreenParams,
28
+                       TitleBarBackButtonListener titleBarBackButtonListener,
29
+                       ScreenAnimator screenAnimator) {
28 30
         super(activity);
29 31
         this.activity = activity;
30 32
         this.titleBarBackButtonListener = titleBarBackButtonListener;
31
-        setLayoutTransition(new LayoutTransition());
32
-        pushInitialScreen(initialScreenParams);
33
+//        setLayoutTransition(new LayoutTransition());
34
+        pushInitialScreen(screenAnimator, initialScreenParams);
33 35
     }
34 36
 
35
-    private void pushInitialScreen(ScreenParams initialScreenParams) {
36
-        addScreen(initialScreenParams);
37
+    private void pushInitialScreen(ScreenAnimator screenAnimator, ScreenParams initialScreenParams) {
38
+        Screen initialScreen = ScreenFactory.create(activity, initialScreenParams, titleBarBackButtonListener);
39
+        addScreen(initialScreen);
40
+        screenAnimator.show(initialScreen);
37 41
     }
38 42
 
39
-    public void push(ScreenParams screenParams) {
40
-        Screen previous = stack.peek();
41
-        addScreen(screenParams);
42
-        removePreviousWithoutUnmount(previous);
43
+    public void push(ScreenAnimator screenAnimator, ScreenParams params) {
44
+        Screen nextScreen = ScreenFactory.create(activity, params, titleBarBackButtonListener);
45
+        final Screen previousScreen = stack.peek();
46
+        addScreen(nextScreen);
47
+        screenAnimator.show(nextScreen, new Runnable() {
48
+            @Override
49
+            public void run() {
50
+                removePreviousWithoutUnmount(previousScreen);
51
+            }
52
+        });
43 53
     }
44 54
 
45
-    private void addScreen(ScreenParams screenParams) {
46
-        Screen screen = ScreenFactory.create(activity, screenParams, titleBarBackButtonListener);
55
+    private void addScreen(Screen screen) {
56
+        screen.setVisibility(INVISIBLE);
47 57
         addView(screen, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
48 58
         stack.push(screen);
49 59
     }
@@ -53,28 +63,32 @@ public class ScreenStack extends FrameLayout {
53 63
         removeView(previous);
54 64
     }
55 65
 
56
-    public void pop() {
66
+    public void pop(ScreenAnimator screenAnimator) {
57 67
         if (!canPop()) {
58 68
             return;
59 69
         }
60 70
 
61
-        Screen toRemove = stack.pop();
71
+        final Screen toRemove = stack.pop();
62 72
         Screen previous = stack.peek();
63 73
 
64 74
         readdPrevious(previous);
65
-
66
-        toRemove.ensureUnmountOnDetachedFromWindow();
67
-        removeView(toRemove);
75
+        screenAnimator.hide(toRemove, new Runnable() {
76
+            @Override
77
+            public void run() {
78
+                toRemove.ensureUnmountOnDetachedFromWindow();
79
+                removeView(toRemove);
80
+            }
81
+        });
68 82
     }
69 83
 
70 84
     private void readdPrevious(Screen previous) {
71
-        addView(previous, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
85
+        addView(previous, 0, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
72 86
         previous.preventMountAfterReattachedToWindow();
73 87
     }
74 88
 
75
-    public void popToRoot() {
89
+    public void popToRoot(ScreenAnimator screenAnimator) {
76 90
         while (canPop()) {
77
-            pop();
91
+            pop(screenAnimator);
78 92
         }
79 93
     }
80 94
 

+ 10
- 0
android/app/src/main/java/com/reactnativenavigation/utils/ViewUtils.java View File

@@ -1,11 +1,14 @@
1 1
 package com.reactnativenavigation.utils;
2 2
 
3
+import android.content.Context;
3 4
 import android.graphics.Color;
4 5
 import android.graphics.PorterDuff;
5 6
 import android.graphics.PorterDuffColorFilter;
6 7
 import android.graphics.drawable.Drawable;
8
+import android.util.DisplayMetrics;
7 9
 import android.view.View;
8 10
 import android.view.ViewTreeObserver;
11
+import android.view.WindowManager;
9 12
 
10 13
 import com.reactnativenavigation.NavigationApplication;
11 14
 
@@ -40,5 +43,12 @@ public class ViewUtils {
40 43
     public static int generateViewId() {
41 44
         return viewId.incrementAndGet();
42 45
     }
46
+
47
+    public static float getScreenHeight() {
48
+        WindowManager wm = (WindowManager) NavigationApplication.instance.getSystemService(Context.WINDOW_SERVICE);
49
+        DisplayMetrics metrics = new DisplayMetrics();
50
+        wm.getDefaultDisplay().getMetrics(metrics);
51
+        return metrics.heightPixels;
52
+    }
43 53
 }
44 54