Browse Source

Modal animations (#1856)

This commit adds support for fade and slide-horizontal animations for modals and a few other animation related changes:

* Support defining animationType when calling dismissModal
* Support fade and slide horizontal animations for modals
* Slightly reworked current screen animations timings
* Fixed slide-horizontal x offset and removed alpha
Guy Carmeli 7 years ago
parent
commit
89eb30a542

+ 3
- 2
android/app/src/main/java/com/reactnativenavigation/bridge/NavigationReactModule.java View File

@@ -18,6 +18,7 @@ import com.reactnativenavigation.params.TitleBarLeftButtonParams;
18 18
 import com.reactnativenavigation.params.parsers.ContextualMenuParamsParser;
19 19
 import com.reactnativenavigation.params.parsers.FabParamsParser;
20 20
 import com.reactnativenavigation.params.parsers.LightBoxParamsParser;
21
+import com.reactnativenavigation.params.parsers.ScreenParamsParser;
21 22
 import com.reactnativenavigation.params.parsers.SlidingOverlayParamsParser;
22 23
 import com.reactnativenavigation.params.parsers.SnackbarParamsParser;
23 24
 import com.reactnativenavigation.params.parsers.TitleBarButtonParamsParser;
@@ -216,8 +217,8 @@ public class NavigationReactModule extends ReactContextBaseJavaModule {
216 217
     }
217 218
 
218 219
     @ReactMethod
219
-    public void dismissTopModal() {
220
-        NavigationCommandsHandler.dismissTopModal();
220
+    public void dismissTopModal(final ReadableMap params) {
221
+        NavigationCommandsHandler.dismissTopModal(ScreenParamsParser.parse(BundleConverter.toBundle(params)));
221 222
     }
222 223
 
223 224
     @ReactMethod

+ 23
- 11
android/app/src/main/java/com/reactnativenavigation/controllers/Modal.java View File

@@ -21,11 +21,12 @@ import com.reactnativenavigation.params.ScreenParams;
21 21
 import com.reactnativenavigation.params.SlidingOverlayParams;
22 22
 import com.reactnativenavigation.params.TitleBarButtonParams;
23 23
 import com.reactnativenavigation.params.TitleBarLeftButtonParams;
24
+import com.reactnativenavigation.params.parsers.ModalAnimationFactory;
24 25
 import com.reactnativenavigation.screens.NavigationType;
25 26
 
26 27
 import java.util.List;
27 28
 
28
-public class Modal extends Dialog implements DialogInterface.OnDismissListener, ScreenStackContainer {
29
+class Modal extends Dialog implements DialogInterface.OnDismissListener, ScreenStackContainer {
29 30
 
30 31
     private final AppCompatActivity activity;
31 32
     private final OnModalDismissedListener onModalDismissedListener;
@@ -104,13 +105,13 @@ public class Modal extends Dialog implements DialogInterface.OnDismissListener,
104 105
         void onModalDismissed(Modal modal);
105 106
     }
106 107
 
107
-    public Modal(AppCompatActivity activity, OnModalDismissedListener onModalDismissedListener, ScreenParams screenParams) {
108
+    Modal(AppCompatActivity activity, OnModalDismissedListener onModalDismissedListener, ScreenParams screenParams) {
108 109
         super(activity, R.style.Modal);
109 110
         this.activity = activity;
110 111
         this.onModalDismissedListener = onModalDismissedListener;
111 112
         this.screenParams = screenParams;
112 113
         createContent();
113
-        setAnimation();
114
+        setAnimation(screenParams);
114 115
     }
115 116
 
116 117
     public AppCompatActivity getActivity() {
@@ -128,18 +129,19 @@ public class Modal extends Dialog implements DialogInterface.OnDismissListener,
128 129
     }
129 130
 
130 131
     private void setWindowFlags() {
131
-        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
132
+        Window window = getWindow();
133
+        if (window == null) return;
134
+        window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
132 135
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
133
-            getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
136
+            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
134 137
         }
135 138
     }
136 139
 
137
-    private void setAnimation() {
138
-        if (!screenParams.animateScreenTransitions) {
139
-            if (getWindow() != null) {
140
-                getWindow().setWindowAnimations(android.R.style.Animation);
141
-            }
142
-        }
140
+    private void setAnimation(ScreenParams screenParams) {
141
+        if (getWindow() == null) return;
142
+        final WindowManager.LayoutParams attributes = getWindow().getAttributes();
143
+        attributes.windowAnimations = ModalAnimationFactory.create(screenParams);
144
+        getWindow().setAttributes(attributes);
143 145
     }
144 146
 
145 147
     @Override
@@ -179,6 +181,16 @@ public class Modal extends Dialog implements DialogInterface.OnDismissListener,
179 181
         }
180 182
     }
181 183
 
184
+    void dismiss(ScreenParams params) {
185
+        setAnimation(params);
186
+        NavigationApplication.instance.runOnMainThread(new Runnable() {
187
+            @Override
188
+            public void run() {
189
+                dismiss();
190
+            }
191
+        });
192
+    }
193
+
182 194
     @Override
183 195
     public void dismiss() {
184 196
         if (!isDestroyed) {

+ 2
- 2
android/app/src/main/java/com/reactnativenavigation/controllers/ModalController.java View File

@@ -41,9 +41,9 @@ class ModalController implements ScreenStackContainer, Modal.OnModalDismissedLis
41 41
         stack.add(modal);
42 42
     }
43 43
 
44
-    void dismissTopModal() {
44
+    void dismissTopModal(ScreenParams params) {
45 45
         if (isShowing()) {
46
-            stack.pop().dismiss();
46
+            stack.pop().dismiss(params);
47 47
         }
48 48
     }
49 49
 

+ 2
- 2
android/app/src/main/java/com/reactnativenavigation/controllers/NavigationActivity.java View File

@@ -244,8 +244,8 @@ public class NavigationActivity extends AppCompatActivity implements DefaultHard
244 244
         modalController.showModal(screenParams);
245 245
     }
246 246
 
247
-    void dismissTopModal() {
248
-        modalController.dismissTopModal();
247
+    void dismissTopModal(ScreenParams params) {
248
+        modalController.dismissTopModal(params);
249 249
     }
250 250
 
251 251
     void dismissAllModals() {

+ 2
- 2
android/app/src/main/java/com/reactnativenavigation/controllers/NavigationCommandsHandler.java View File

@@ -263,7 +263,7 @@ public class NavigationCommandsHandler {
263 263
         });
264 264
     }
265 265
 
266
-    public static void dismissTopModal() {
266
+    public static void dismissTopModal(final ScreenParams params) {
267 267
         final NavigationActivity currentActivity = NavigationActivity.currentActivity;
268 268
         if (currentActivity == null) {
269 269
             return;
@@ -272,7 +272,7 @@ public class NavigationCommandsHandler {
272 272
         NavigationApplication.instance.runOnMainThread(new Runnable() {
273 273
             @Override
274 274
             public void run() {
275
-                currentActivity.dismissTopModal();
275
+                currentActivity.dismissTopModal(params);
276 276
             }
277 277
         });
278 278
     }

+ 18
- 0
android/app/src/main/java/com/reactnativenavigation/params/parsers/ModalAnimationFactory.java View File

@@ -0,0 +1,18 @@
1
+package com.reactnativenavigation.params.parsers;
2
+
3
+import com.reactnativenavigation.R;
4
+import com.reactnativenavigation.params.ScreenParams;
5
+
6
+public class ModalAnimationFactory {
7
+    public static int create(ScreenParams params) {
8
+        if (!params.animateScreenTransitions) return R.style.ModalNoAnimation;
9
+        switch (params.animationType) {
10
+            case "fade":
11
+                return R.style.ModalFadeAnimation;
12
+            case "slide-horizontal":
13
+                return R.style.ModalSlideHorizontal;
14
+            default:
15
+                return R.style.ModalDefaultAnimations;
16
+        }
17
+    }
18
+}

+ 1
- 1
android/app/src/main/java/com/reactnativenavigation/params/parsers/ScreenParamsParser.java View File

@@ -55,7 +55,7 @@ public class ScreenParamsParser extends Parser {
55 55
         result.animateScreenTransitions = new AnimationParser(params).parse();
56 56
         result.sharedElementsTransitions = getSharedElementsTransitions(params);
57 57
 
58
-        result.animationType = params.getString(ANIMATION_TYPE);
58
+        result.animationType = params.getString(ANIMATION_TYPE, "slide-up");
59 59
 
60 60
         return result;
61 61
     }

+ 30
- 22
android/app/src/main/java/com/reactnativenavigation/screens/ScreenAnimator.java View File

@@ -5,18 +5,28 @@ import android.animation.AnimatorListenerAdapter;
5 5
 import android.animation.AnimatorSet;
6 6
 import android.animation.ObjectAnimator;
7 7
 import android.view.View;
8
+import android.view.animation.AccelerateDecelerateInterpolator;
8 9
 import android.view.animation.AccelerateInterpolator;
9 10
 import android.view.animation.DecelerateInterpolator;
10 11
 import android.view.animation.LinearInterpolator;
12
+
11 13
 import com.reactnativenavigation.NavigationApplication;
12 14
 import com.reactnativenavigation.utils.ViewUtils;
13 15
 import com.reactnativenavigation.views.sharedElementTransition.SharedElementsAnimator;
14 16
 
15
-import javax.annotation.Nullable;
16 17
 import java.util.ArrayList;
17 18
 import java.util.List;
18 19
 
20
+import javax.annotation.Nullable;
21
+
19 22
 class ScreenAnimator {
23
+    private static final int DURATION = 250;
24
+    private static final int ALPHA_START_DELAY = 100;
25
+    private static final int ALPHA_SHORT_DURATION = 150;
26
+    private static final LinearInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
27
+    private static final DecelerateInterpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
28
+    private static final AccelerateDecelerateInterpolator ACCELERATE_DECELERATE_INTERPOLATOR = new AccelerateDecelerateInterpolator();
29
+    private static final AccelerateInterpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator();
20 30
     private final float translationY;
21 31
     private final float translationX;
22 32
     private Screen screen;
@@ -24,7 +34,7 @@ class ScreenAnimator {
24 34
     ScreenAnimator(Screen screen) {
25 35
         this.screen = screen;
26 36
         translationY = 0.08f * ViewUtils.getWindowHeight(screen.activity);
27
-        translationX = 0.08f * ViewUtils.getWindowWidth(screen.activity);
37
+        translationX = ViewUtils.getWindowWidth(screen.activity);
28 38
     }
29 39
 
30 40
     public void show(boolean animate, final Runnable onAnimationEnd) {
@@ -32,7 +42,7 @@ class ScreenAnimator {
32 42
             createShowAnimator(onAnimationEnd).start();
33 43
         } else {
34 44
             screen.setVisibility(View.VISIBLE);
35
-            NavigationApplication.instance.runOnMainThread(onAnimationEnd, 200);
45
+            NavigationApplication.instance.runOnMainThread(onAnimationEnd, DURATION);
36 46
         }
37 47
     }
38 48
 
@@ -55,28 +65,27 @@ class ScreenAnimator {
55 65
 
56 66
     private Animator createShowAnimator(final @Nullable Runnable onAnimationEnd) {
57 67
         ObjectAnimator alpha = ObjectAnimator.ofFloat(screen, View.ALPHA, 0, 1);
58
-        alpha.setInterpolator(new DecelerateInterpolator());
59
-        alpha.setDuration(200);
68
+        alpha.setInterpolator(DECELERATE_INTERPOLATOR);
60 69
 
61 70
         AnimatorSet set = new AnimatorSet();
62 71
         switch (String.valueOf(this.screen.screenParams.animationType)) {
63 72
             case "fade": {
73
+                alpha.setDuration(DURATION);
64 74
                 set.play(alpha);
65 75
                 break;
66 76
             }
67 77
             case "slide-horizontal": {
68 78
                 ObjectAnimator translationX = ObjectAnimator.ofFloat(screen, View.TRANSLATION_X, this.translationX, 0);
69
-                translationX.setInterpolator(new DecelerateInterpolator());
70
-                translationX.setDuration(280);
71
-
72
-                set.playTogether(translationX, alpha);
79
+                translationX.setInterpolator(ACCELERATE_DECELERATE_INTERPOLATOR);
80
+                translationX.setDuration(DURATION);
81
+                set.play(translationX);
73 82
                 break;
74 83
             }
75 84
             default: {
76 85
                 ObjectAnimator translationY = ObjectAnimator.ofFloat(screen, View.TRANSLATION_Y, this.translationY, 0);
77
-                translationY.setInterpolator(new DecelerateInterpolator());
78
-                translationY.setDuration(280);
79
-
86
+                translationY.setInterpolator(DECELERATE_INTERPOLATOR);
87
+                translationY.setDuration(DURATION);
88
+                alpha.setDuration(ALPHA_SHORT_DURATION);
80 89
                 set.playTogether(translationY, alpha);
81 90
                 break;
82 91
             }
@@ -100,29 +109,28 @@ class ScreenAnimator {
100 109
 
101 110
     private Animator createHideAnimator(final Runnable onAnimationEnd) {
102 111
         ObjectAnimator alpha = ObjectAnimator.ofFloat(screen, View.ALPHA, 0);
103
-        alpha.setInterpolator(new LinearInterpolator());
104
-        alpha.setStartDelay(100);
105
-        alpha.setDuration(150);
112
+        alpha.setInterpolator(DECELERATE_INTERPOLATOR);
106 113
 
107 114
         AnimatorSet set = new AnimatorSet();
108 115
         switch (String.valueOf(this.screen.screenParams.animationType)) {
109 116
             case "fade": {
117
+                alpha.setDuration(DURATION);
110 118
                 set.play(alpha);
111 119
                 break;
112 120
             }
113 121
             case "slide-horizontal": {
114 122
                 ObjectAnimator translationX = ObjectAnimator.ofFloat(screen, View.TRANSLATION_X, this.translationX);
115
-                translationX.setInterpolator(new AccelerateInterpolator());
116
-                translationX.setDuration(250);
117
-
118
-                set.playTogether(translationX, alpha);
123
+                translationX.setInterpolator(ACCELERATE_INTERPOLATOR);
124
+                translationX.setDuration(DURATION);
125
+                set.play(translationX);
119 126
                 break;
120 127
             }
121 128
             default: {
122 129
                 ObjectAnimator translationY = ObjectAnimator.ofFloat(screen, View.TRANSLATION_Y, this.translationY);
123
-                translationY.setInterpolator(new AccelerateInterpolator());
124
-                translationY.setDuration(250);
125
-
130
+                translationY.setInterpolator(ACCELERATE_INTERPOLATOR);
131
+                translationY.setDuration(DURATION);
132
+                alpha.setStartDelay(ALPHA_START_DELAY);
133
+                alpha.setDuration(ALPHA_SHORT_DURATION);
126 134
                 set.playTogether(translationY, alpha);
127 135
                 break;
128 136
             }

+ 6
- 0
android/app/src/main/res/anim/no_animation.xml View File

@@ -0,0 +1,6 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
3
+       android:fromAlpha="0"
4
+       android:toAlpha="0"
5
+       android:duration="0"
6
+    />

+ 5
- 3
android/app/src/main/res/anim/slide_down.xml View File

@@ -1,12 +1,14 @@
1 1
 <?xml version="1.0" encoding="utf-8"?>
2 2
 <set xmlns:android="http://schemas.android.com/apk/res/android"
3
-    android:interpolator="@android:anim/accelerate_interpolator"
4
-    android:shareInterpolator="true"
5
-    android:duration="190">
3
+     android:duration="@integer/config_activityDefaultDur"
4
+     android:interpolator="@android:anim/accelerate_interpolator"
5
+     android:shareInterpolator="true">
6 6
     <translate
7 7
         android:fromYDelta="0"
8 8
         android:toYDelta="100%"/>
9 9
     <alpha
10
+        android:startOffset="100"
11
+        android:duration="150"
10 12
         android:fromAlpha="1"
11 13
         android:toAlpha="0"/>
12 14
 </set>

+ 6
- 0
android/app/src/main/res/anim/slide_in_right.xml View File

@@ -0,0 +1,6 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<translate
3
+    xmlns:android="http://schemas.android.com/apk/res/android"
4
+    android:duration="@integer/config_activityDefaultDur"
5
+    android:fromXDelta="0"
6
+    android:toXDelta="100%p"/>

+ 6
- 0
android/app/src/main/res/anim/slide_out_right.xml View File

@@ -0,0 +1,6 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<translate
3
+    xmlns:android="http://schemas.android.com/apk/res/android"
4
+    android:duration="@integer/config_activityDefaultDur"
5
+    android:fromXDelta="0"
6
+    android:toXDelta="100%p"/>

+ 1
- 1
android/app/src/main/res/anim/slide_up.xml View File

@@ -5,5 +5,5 @@
5 5
     <translate
6 6
         android:fromYDelta="100%"
7 7
         android:toYDelta="0"
8
-        android:duration="250"/>
8
+        android:duration="@integer/config_activityDefaultDur"/>
9 9
 </set>

+ 4
- 0
android/app/src/main/res/values/config.xml View File

@@ -0,0 +1,4 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<resources>
3
+    <integer name="config_activityDefaultDur">250</integer>
4
+</resources>

+ 16
- 3
android/app/src/main/res/values/styles.xml View File

@@ -1,16 +1,29 @@
1 1
 <?xml version="1.0" encoding="utf-8"?>
2 2
 <resources xmlns:tools="http://schemas.android.com/tools">
3 3
     <style name="Modal" parent="@android:style/Theme.Translucent.NoTitleBar">
4
-        <item name="android:windowAnimationStyle">@style/modalAnimations</item>
5 4
         <item name="android:statusBarColor" tools:targetApi="lollipop">@android:color/transparent</item>
6 5
     </style>
7 6
 
8 7
     <style name="LightBox" parent="@android:style/Theme.Translucent.NoTitleBar">
9
-        <item name="android:windowAnimationStyle">@style/modalAnimations</item>
8
+        <item name="android:windowAnimationStyle">@style/ModalDefaultAnimations</item>
10 9
         <item name="android:statusBarColor" tools:targetApi="lollipop">@android:color/transparent</item>
11 10
     </style>
12 11
 
13
-    <style name="modalAnimations">
12
+    <style name="ModalDefaultAnimations">
13
+        <item name="android:windowEnterAnimation">@anim/slide_up</item>
14 14
         <item name="android:windowExitAnimation">@anim/slide_down</item>
15 15
     </style>
16
+
17
+    <style name="ModalFadeAnimation">
18
+        <item name="android:windowExitAnimation">@android:anim/fade_out</item>
19
+    </style>
20
+
21
+    <style name="ModalNoAnimation">
22
+        <item name="android:windowEnterAnimation">@anim/no_animation</item>
23
+        <item name="android:windowExitAnimation">@anim/no_animation</item>
24
+    </style>
25
+
26
+    <style name="ModalSlideHorizontal">
27
+        <item name="android:windowExitAnimation">@anim/slide_out_right</item>
28
+    </style>
16 29
 </resources>

+ 5
- 2
src/deprecated/platformSpecificDeprecated.android.js View File

@@ -478,8 +478,11 @@ function dismissLightBox() {
478 478
   newPlatformSpecific.dismissLightBox();
479 479
 }
480 480
 
481
-function dismissModal() {
482
-  newPlatformSpecific.dismissTopModal();
481
+function dismissModal(params) {
482
+  newPlatformSpecific.dismissTopModal({
483
+    ...params,
484
+    navigationParams: {}
485
+  });
483 486
 }
484 487
 
485 488
 function dismissAllModals(params) {

+ 2
- 2
src/platformSpecific.android.js View File

@@ -62,8 +62,8 @@ function dismissLightBox() {
62 62
   NativeReactModule.dismissLightBox();
63 63
 }
64 64
 
65
-function dismissTopModal() {
66
-  NativeReactModule.dismissTopModal();
65
+function dismissTopModal(params) {
66
+  NativeReactModule.dismissTopModal(params);
67 67
 }
68 68
 
69 69
 function dismissAllModals() {