Browse Source

Topbar anim (#2904)

* WIP

* Apply default options when screen options are null

* modal animations

* fix tests

* refactor

* refactor

* refactor

* refactor

* test fix

* topBar anim

* child disappear bug

* Update app.js

* Update WelcomeScreen.js

* fix tests
Roman Kozlov 6 years ago
parent
commit
44b5a1e700

+ 4
- 11
lib/android/app/src/main/java/com/reactnativenavigation/anim/NavigationAnimator.java View File

@@ -3,16 +3,9 @@ package com.reactnativenavigation.anim;
3 3
 import android.animation.Animator;
4 4
 import android.animation.AnimatorListenerAdapter;
5 5
 import android.animation.AnimatorSet;
6
-import android.animation.ObjectAnimator;
7 6
 import android.content.Context;
8
-import android.support.annotation.NonNull;
9 7
 import android.support.annotation.Nullable;
10 8
 import android.view.View;
11
-import android.view.animation.AccelerateInterpolator;
12
-import android.view.animation.DecelerateInterpolator;
13
-
14
-import com.reactnativenavigation.parse.AnimationsOptions;
15
-import com.reactnativenavigation.utils.UiUtils;
16 9
 
17 10
 @SuppressWarnings("ResourceType")
18 11
 public class NavigationAnimator extends BaseAnimator {
@@ -24,8 +17,8 @@ public class NavigationAnimator extends BaseAnimator {
24 17
     public void animatePush(final View view, @Nullable final AnimationListener animationListener) {
25 18
         view.setVisibility(View.INVISIBLE);
26 19
         AnimatorSet set;
27
-        if (options.push.hasValue()) {
28
-            set = options.push.getAnimation(view);
20
+        if (options.push.content.hasValue()) {
21
+            set = options.push.content.getAnimation(view);
29 22
         } else {
30 23
             set = getDefaultPushAnimation(view);
31 24
         }
@@ -47,8 +40,8 @@ public class NavigationAnimator extends BaseAnimator {
47 40
 
48 41
     public void animatePop(View view, @Nullable final AnimationListener animationListener) {
49 42
         AnimatorSet set;
50
-        if (options.pop.hasValue()) {
51
-            set = options.pop.getAnimation(view);
43
+        if (options.pop.content.hasValue()) {
44
+            set = options.pop.content.getAnimation(view);
52 45
         } else {
53 46
             set = getDefaultPopAnimation(view);
54 47
         }

+ 48
- 32
lib/android/app/src/main/java/com/reactnativenavigation/anim/TopBarAnimator.java View File

@@ -3,82 +3,98 @@ package com.reactnativenavigation.anim;
3 3
 
4 4
 import android.animation.Animator;
5 5
 import android.animation.AnimatorListenerAdapter;
6
+import android.animation.AnimatorSet;
6 7
 import android.animation.ObjectAnimator;
7 8
 import android.animation.TimeInterpolator;
8 9
 import android.view.View;
9
-import android.view.ViewGroup;
10 10
 import android.view.animation.AccelerateInterpolator;
11 11
 import android.view.animation.DecelerateInterpolator;
12 12
 
13
+import com.reactnativenavigation.parse.AnimationOptions;
13 14
 import com.reactnativenavigation.views.TopBar;
14 15
 
15 16
 public class TopBarAnimator {
16 17
 
18
+    private static final int DEFAULT_COLLAPSE_DURATION = 100;
17 19
     private static final int DURATION_TOPBAR = 300;
18 20
     private final DecelerateInterpolator decelerateInterpolator = new DecelerateInterpolator();
19 21
     private final AccelerateInterpolator accelerateInterpolator = new AccelerateInterpolator();
20 22
 
21 23
     private TopBar topBar;
22
-    private View contentView;
23
-    private ObjectAnimator hideAnimator;
24
-    private ObjectAnimator showAnimator;
24
+    private AnimatorSet hideAnimator;
25
+    private AnimatorSet showAnimator;
25 26
 
26 27
     public TopBarAnimator(TopBar topBar) {
27 28
         this.topBar = topBar;
28 29
     }
29 30
 
30
-    public void show() {
31
-        show(-1 * topBar.getMeasuredHeight(), decelerateInterpolator, DURATION_TOPBAR);
31
+    public void show(AnimationOptions options) {
32
+        if (options.hasValue()) {
33
+            showAnimator = options.getAnimation(topBar);
34
+        } else {
35
+            showAnimator = getDefaultShowAnimator(-1 * topBar.getMeasuredHeight(), decelerateInterpolator, DURATION_TOPBAR);
36
+        }
37
+        show();
32 38
     }
33 39
 
34
-    public void show(float startTranslation, TimeInterpolator interpolator, int duration) {
35
-        showAnimator = ObjectAnimator.ofFloat(topBar, View.TRANSLATION_Y, startTranslation, 0);
36
-        showAnimator.setInterpolator(interpolator);
37
-        showAnimator.setDuration(duration);
40
+    public void show(float startTranslation) {
41
+        showAnimator = getDefaultShowAnimator(startTranslation, null, DEFAULT_COLLAPSE_DURATION);
42
+        show();
43
+    }
38 44
 
45
+    private void show() {
39 46
         showAnimator.addListener(new AnimatorListenerAdapter() {
40
-
41 47
             @Override
42 48
             public void onAnimationStart(Animator animation) {
43 49
                 topBar.setVisibility(View.VISIBLE);
44 50
             }
45
-
46
-            @Override
47
-            public void onAnimationEnd(Animator animation) {
48
-                if (contentView != null) {
49
-                    ViewGroup.LayoutParams layoutParams = contentView.getLayoutParams();
50
-                    layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
51
-                    contentView.setLayoutParams(layoutParams);
52
-                }
53
-            }
54 51
         });
55 52
         showAnimator.start();
56 53
     }
57 54
 
58
-    public void hide() {
59
-        hide(0, accelerateInterpolator, DURATION_TOPBAR);
55
+    private AnimatorSet getDefaultShowAnimator(float startTranslation, TimeInterpolator interpolator, int duration) {
56
+        ObjectAnimator showAnimator = ObjectAnimator.ofFloat(topBar, View.TRANSLATION_Y, startTranslation, 0);
57
+        showAnimator.setInterpolator(interpolator);
58
+        showAnimator.setDuration(duration);
59
+        AnimatorSet set = new AnimatorSet();
60
+        set.play(showAnimator);
61
+        return set;
62
+    }
63
+
64
+    public void hide(AnimationOptions options, AnimationListener listener) {
65
+        if (options.hasValue()) {
66
+            hideAnimator = options.getAnimation(topBar);
67
+        } else {
68
+            hideAnimator = getDefaultHideAnimator(0, accelerateInterpolator, DURATION_TOPBAR);
69
+        }
70
+        hide(listener);
60 71
     }
61 72
 
62
-    void hide(float startTranslation, TimeInterpolator interpolator, int duration) {
63
-        hideAnimator = ObjectAnimator.ofFloat(topBar, View.TRANSLATION_Y, startTranslation, -1 * topBar.getMeasuredHeight());
64
-        hideAnimator.setInterpolator(interpolator);
65
-        hideAnimator.setDuration(duration);
73
+    void hide(float startTranslation) {
74
+        hideAnimator = getDefaultHideAnimator(startTranslation, null, DEFAULT_COLLAPSE_DURATION);
75
+        hide(null);
76
+    }
66 77
 
78
+    private void hide(AnimationListener listener) {
67 79
         hideAnimator.addListener(new AnimatorListenerAdapter() {
68 80
             @Override
69 81
             public void onAnimationEnd(Animator animation) {
70
-                if (contentView != null) {
71
-                    ViewGroup.LayoutParams layoutParams = contentView.getLayoutParams();
72
-                    layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
73
-                    contentView.setLayoutParams(layoutParams);
74
-                }
75
-
76 82
                 topBar.setVisibility(View.GONE);
83
+                if (listener != null) listener.onAnimationEnd();
77 84
             }
78 85
         });
79 86
         hideAnimator.start();
80 87
     }
81 88
 
89
+    private AnimatorSet getDefaultHideAnimator(float startTranslation, TimeInterpolator interpolator, int duration) {
90
+        ObjectAnimator hideAnimator = ObjectAnimator.ofFloat(topBar, View.TRANSLATION_Y, startTranslation, -1 * topBar.getMeasuredHeight());
91
+        hideAnimator.setInterpolator(interpolator);
92
+        hideAnimator.setDuration(duration);
93
+        AnimatorSet set = new AnimatorSet();
94
+        set.play(hideAnimator);
95
+        return set;
96
+    }
97
+
82 98
     public boolean isRunning() {
83 99
         return (hideAnimator != null && hideAnimator.isRunning()) || (showAnimator != null && showAnimator.isRunning());
84 100
     }

+ 2
- 2
lib/android/app/src/main/java/com/reactnativenavigation/anim/TopBarCollapseBehavior.java View File

@@ -51,11 +51,11 @@ public class TopBarCollapseBehavior implements ScrollEventListener.OnScrollListe
51 51
 
52 52
     @Override
53 53
     public void onShow() {
54
-        animator.show(topBar.getTranslationY(), null, 100);
54
+        animator.show(topBar.getTranslationY());
55 55
     }
56 56
 
57 57
     @Override
58 58
     public void onHide() {
59
-        animator.hide(topBar.getTranslationY(), null, 100);
59
+        animator.hide(topBar.getTranslationY());
60 60
     }
61 61
 }

+ 6
- 0
lib/android/app/src/main/java/com/reactnativenavigation/interfaces/ChildDisappearListener.java View File

@@ -0,0 +1,6 @@
1
+package com.reactnativenavigation.interfaces;
2
+
3
+
4
+public interface ChildDisappearListener {
5
+    void childDisappear();
6
+}

+ 4
- 5
lib/android/app/src/main/java/com/reactnativenavigation/parse/AnimationsOptions.java View File

@@ -11,8 +11,8 @@ public class AnimationsOptions {
11 11
         AnimationsOptions options = new AnimationsOptions();
12 12
         if (json == null) return options;
13 13
 
14
-        options.push = AnimationOptions.parse(json.optJSONObject("push"));
15
-        options.pop = AnimationOptions.parse(json.optJSONObject("pop"));
14
+        options.push = NestedAnimationsOptions.parse(json.optJSONObject("push"));
15
+        options.pop = NestedAnimationsOptions.parse(json.optJSONObject("pop"));
16 16
         options.startApp = AnimationOptions.parse(json.optJSONObject("startApp"));
17 17
         options.showModal = AnimationOptions.parse(json.optJSONObject("showModal"));
18 18
         options.dismissModal = AnimationOptions.parse(json.optJSONObject("dismissModal"));
@@ -20,8 +20,8 @@ public class AnimationsOptions {
20 20
         return options;
21 21
     }
22 22
 
23
-    public AnimationOptions push = new AnimationOptions();
24
-    public AnimationOptions pop = new AnimationOptions();
23
+    public NestedAnimationsOptions push = new NestedAnimationsOptions();
24
+    public NestedAnimationsOptions pop = new NestedAnimationsOptions();
25 25
     public AnimationOptions startApp = new AnimationOptions();
26 26
     public AnimationOptions showModal = new AnimationOptions();
27 27
     public AnimationOptions dismissModal = new AnimationOptions();
@@ -32,7 +32,6 @@ public class AnimationsOptions {
32 32
         startApp.mergeWith(other.startApp);
33 33
         showModal.mergeWith(other.showModal);
34 34
         dismissModal.mergeWith(other.dismissModal);
35
-
36 35
     }
37 36
 
38 37
     void mergeWithDefault(AnimationsOptions defaultOptions) {

+ 33
- 0
lib/android/app/src/main/java/com/reactnativenavigation/parse/NestedAnimationsOptions.java View File

@@ -0,0 +1,33 @@
1
+package com.reactnativenavigation.parse;
2
+
3
+
4
+import org.json.JSONObject;
5
+
6
+public class NestedAnimationsOptions {
7
+    public static NestedAnimationsOptions parse(JSONObject json) {
8
+        NestedAnimationsOptions options = new NestedAnimationsOptions();
9
+        if (json == null) return options;
10
+
11
+        options.content = AnimationOptions.parse(json.optJSONObject("content"));
12
+        options.bottomTabs = AnimationOptions.parse(json.optJSONObject("bottomTabs"));
13
+        options.topBar = AnimationOptions.parse(json.optJSONObject("topBar"));
14
+
15
+        return options;
16
+    }
17
+
18
+    public AnimationOptions content = new AnimationOptions();
19
+    public AnimationOptions bottomTabs = new AnimationOptions();
20
+    public AnimationOptions topBar = new AnimationOptions();
21
+
22
+    void mergeWith(NestedAnimationsOptions other) {
23
+        topBar.mergeWith(other.topBar);
24
+        content.mergeWith(other.content);
25
+        bottomTabs.mergeWith(other.bottomTabs);
26
+    }
27
+
28
+    void mergeWithDefault(NestedAnimationsOptions defaultOptions) {
29
+        content.mergeWithDefault(defaultOptions.content);
30
+        bottomTabs.mergeWithDefault(defaultOptions.bottomTabs);
31
+        topBar.mergeWithDefault(defaultOptions.topBar);
32
+    }
33
+}

+ 26
- 7
lib/android/app/src/main/java/com/reactnativenavigation/presentation/OptionsPresenter.java View File

@@ -1,7 +1,10 @@
1 1
 package com.reactnativenavigation.presentation;
2 2
 
3 3
 import android.app.Activity;
4
+import android.support.annotation.NonNull;
4 5
 
6
+import com.reactnativenavigation.interfaces.ChildDisappearListener;
7
+import com.reactnativenavigation.parse.AnimationsOptions;
5 8
 import com.reactnativenavigation.parse.Options;
6 9
 import com.reactnativenavigation.parse.OrientationOptions;
7 10
 import com.reactnativenavigation.parse.TopBarOptions;
@@ -24,7 +27,7 @@ public class OptionsPresenter {
24 27
     public void applyChildOptions(Options options, Component child) {
25 28
         applyOrientation(options.orientationOptions);
26 29
         applyButtons(options.topBarOptions.leftButtons, options.topBarOptions.rightButtons);
27
-        applyTopBarOptions(options.topBarOptions, child);
30
+        applyTopBarOptions(options.topBarOptions, child, options.animationsOptions);
28 31
         applyTopTabsOptions(options.topTabsOptions);
29 32
         applyTopTabOptions(options.topTabOptions);
30 33
     }
@@ -33,9 +36,10 @@ public class OptionsPresenter {
33 36
         ((Activity) topBar.getContext()).setRequestedOrientation(options.getValue());
34 37
     }
35 38
 
36
-    private void applyTopBarOptions(TopBarOptions options, Component component) {
39
+    private void applyTopBarOptions(TopBarOptions options, Component component, AnimationsOptions animationOptions) {
37 40
         if (options.title.text.hasValue()) topBar.setTitle(options.title.text.get());
38
-        if (options.title.component.hasValue()) topBar.setComponent(options.title.component.get(), options.title.alignment);
41
+        if (options.title.component.hasValue())
42
+            topBar.setComponent(options.title.component.get(), options.title.alignment);
39 43
         topBar.setBackgroundColor(options.background.color);
40 44
         topBar.setTitleTextColor(options.title.color);
41 45
         topBar.setTitleFontSize(options.title.fontSize);
@@ -43,10 +47,18 @@ public class OptionsPresenter {
43 47
 
44 48
         topBar.setTitleTypeface(options.title.fontFamily);
45 49
         if (options.visible.isFalse()) {
46
-            topBar.hide(options.animate);
50
+            if (options.animate.isTrueOrUndefined()) {
51
+                topBar.hideAnimate(animationOptions.pop.topBar);
52
+            } else {
53
+                topBar.hide();
54
+            }
47 55
         }
48 56
         if (options.visible.isTrueOrUndefined()) {
49
-            topBar.show(options.animate);
57
+            if (options.animate.isTrueOrUndefined()) {
58
+                topBar.showAnimate(animationOptions.push.topBar);
59
+            } else {
60
+                topBar.show();
61
+            }
50 62
         }
51 63
         if (options.drawBehind.isTrue()) {
52 64
             component.drawBehindTopBar();
@@ -78,9 +90,16 @@ public class OptionsPresenter {
78 90
         }
79 91
     }
80 92
 
81
-    public void onChildWillDisappear(Options disappearing, Options appearing) {
93
+    public void onChildWillDisappear(Options disappearing, Options appearing, @NonNull ChildDisappearListener childDisappearListener) {
82 94
         if (disappearing.topBarOptions.visible.isTrueOrUndefined() && appearing.topBarOptions.visible.isFalse()) {
83
-            topBar.hide(disappearing.topBarOptions.animate);
95
+            if (disappearing.topBarOptions.animate.isTrueOrUndefined()) {
96
+                topBar.hideAnimate(disappearing.animationsOptions.pop.topBar, childDisappearListener::childDisappear);
97
+            } else {
98
+                topBar.hide();
99
+                childDisappearListener.childDisappear();
100
+            }
101
+        } else {
102
+            childDisappearListener.childDisappear();
84 103
         }
85 104
     }
86 105
 }

+ 81
- 77
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/StackController.java View File

@@ -21,7 +21,7 @@ import java.util.Iterator;
21 21
 
22 22
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
23 23
 
24
-public class StackController extends ParentController <StackLayout> {
24
+public class StackController extends ParentController<StackLayout> {
25 25
 
26 26
     private static final NoOpPromise NO_OP = new NoOpPromise();
27 27
     private final IdStack<ViewController> stack = new IdStack<>();
@@ -77,10 +77,10 @@ public class StackController extends ParentController <StackLayout> {
77 77
     public void animatePush(final ViewController child, final Promise promise) {
78 78
         final ViewController toRemove = stack.peek();
79 79
 
80
-		child.setParentController(this);
81
-		stack.push(child.getId(), child);
82
-		View enteringView = child.getView();
83
-		getView().addView(enteringView, MATCH_PARENT, MATCH_PARENT);
80
+        child.setParentController(this);
81
+        stack.push(child.getId(), child);
82
+        View enteringView = child.getView();
83
+        getView().addView(enteringView, MATCH_PARENT, MATCH_PARENT);
84 84
 
85 85
         if (toRemove != null) {
86 86
             animator.animatePush(enteringView, () -> {
@@ -105,97 +105,99 @@ public class StackController extends ParentController <StackLayout> {
105 105
         finishPopping(exitingController.getView(), exitingController, promise);
106 106
     }
107 107
 
108
-	void animatePop(final Promise promise) {
109
-		if (!canPop()) {
110
-			Navigator.rejectPromise(promise);
111
-			return;
112
-		}
108
+    void animatePop(final Promise promise) {
109
+        if (!canPop()) {
110
+            Navigator.rejectPromise(promise);
111
+            return;
112
+        }
113 113
 
114
-		final ViewController exitingController = stack.pop();
114
+        final ViewController exitingController = stack.pop();
115 115
         final ViewController enteringController = stack.peek();
116 116
         popInternal(exitingController, enteringController);
117 117
 
118
-        animator.animatePop(exitingController.getView(), () -> finishPopping(exitingController.getView(), exitingController, promise));
119
-	}
118
+        animator.animatePop(exitingController.getView(), () -> finishPopping(exitingController.getView(),
119
+                exitingController, promise));
120
+    }
120 121
 
121 122
     private void popInternal(ViewController disappearing, ViewController appearing) {
122 123
         disappearing.onViewWillDisappear();
123 124
         appearing.onViewWillAppear();
124
-        getView().onChildWillDisappear(disappearing.options, appearing.options);
125
-        getView().addView(appearing.getView(), getView().indexOfChild(disappearing.getView()));
125
+        getView().onChildWillDisappear(disappearing.options, appearing.options, () ->
126
+                getView().addView(appearing.getView(), getView().indexOfChild(disappearing.getView()))
127
+        );
126 128
     }
127 129
 
128 130
     boolean canPop() {
129 131
         return stack.size() > 1;
130 132
     }
131 133
 
132
-	private void finishPopping(View exitingView, ViewController poppedTop, Promise promise) {
133
-		getView().removeView(exitingView);
134
-		poppedTop.destroy();
134
+    private void finishPopping(View exitingView, ViewController poppedTop, Promise promise) {
135
+        getView().removeView(exitingView);
136
+        poppedTop.destroy();
135 137
         promise.resolve(poppedTop.getId());
136
-	}
137
-
138
-	void popSpecific(final ViewController childController, Promise promise) {
139
-		if (stack.isTop(childController.getId())) {
140
-			animatePop(promise);
141
-		} else {
142
-			stack.remove(childController.getId());
143
-			childController.destroy();
138
+    }
139
+
140
+    void popSpecific(final ViewController childController, Promise promise) {
141
+        if (stack.isTop(childController.getId())) {
142
+            animatePop(promise);
143
+        } else {
144
+            stack.remove(childController.getId());
145
+            childController.destroy();
144 146
             promise.resolve(childController.getId());
145
-		}
146
-	}
147
-
148
-	void popTo(final ViewController viewController, Promise promise) {
149
-		if (!stack.containsId(viewController.getId())) {
150
-			Navigator.rejectPromise(promise);
151
-			return;
152
-		}
153
-
154
-		Iterator<String> iterator = stack.iterator();
155
-		String currentControlId = iterator.next();
156
-		while (!viewController.getId().equals(currentControlId)) {
157
-			String nextControlId = iterator.next();
158
-			boolean animate = nextControlId.equals(viewController.getId());
159
-			if (animate) {
160
-			    animatePop(promise);
147
+        }
148
+    }
149
+
150
+    void popTo(final ViewController viewController, Promise promise) {
151
+        if (!stack.containsId(viewController.getId())) {
152
+            Navigator.rejectPromise(promise);
153
+            return;
154
+        }
155
+
156
+        Iterator<String> iterator = stack.iterator();
157
+        String currentControlId = iterator.next();
158
+        while (!viewController.getId().equals(currentControlId)) {
159
+            String nextControlId = iterator.next();
160
+            boolean animate = nextControlId.equals(viewController.getId());
161
+            if (animate) {
162
+                animatePop(promise);
161 163
             } else {
162
-			    pop(NO_OP);
164
+                pop(NO_OP);
163 165
             }
164
-			currentControlId = nextControlId;
165
-		}
166
-	}
166
+            currentControlId = nextControlId;
167
+        }
168
+    }
167 169
 
168
-	void popToRoot(Promise promise) {
169
-		while (canPop()) {
170
-			boolean animate = stack.size() == 2; // First element is root
170
+    void popToRoot(Promise promise) {
171
+        while (canPop()) {
172
+            boolean animate = stack.size() == 2; // First element is root
171 173
             if (animate) {
172 174
                 animatePop(promise);
173 175
             } else {
174 176
                 pop(NO_OP);
175 177
             }
176
-		}
177
-	}
178
-
179
-	ViewController peek() {
180
-		return stack.peek();
181
-	}
182
-
183
-	public int size() {
184
-		return stack.size();
185
-	}
186
-
187
-	public boolean isEmpty() {
188
-		return stack.isEmpty();
189
-	}
190
-
191
-	@Override
192
-	public boolean handleBack() {
193
-		if (canPop()) {
194
-			animatePop(NO_OP);
195
-			return true;
196
-		}
178
+        }
179
+    }
180
+
181
+    ViewController peek() {
182
+        return stack.peek();
183
+    }
184
+
185
+    public int size() {
186
+        return stack.size();
187
+    }
188
+
189
+    public boolean isEmpty() {
190
+        return stack.isEmpty();
191
+    }
192
+
193
+    @Override
194
+    public boolean handleBack() {
195
+        if (canPop()) {
196
+            animatePop(NO_OP);
197
+            return true;
198
+        }
197 199
         return false;
198
-	}
200
+    }
199 201
 
200 202
     @Override
201 203
     public void sendOnNavigationButtonPressed(String buttonId) {
@@ -208,11 +210,11 @@ public class StackController extends ParentController <StackLayout> {
208 210
         return new StackLayout(getActivity(), topBarButtonCreator, titleBarReactViewCreator, this::sendOnNavigationButtonPressed);
209 211
     }
210 212
 
211
-	@NonNull
212
-	@Override
213
-	public Collection<ViewController> getChildControllers() {
214
-		return stack.values();
215
-	}
213
+    @NonNull
214
+    @Override
215
+    public Collection<ViewController> getChildControllers() {
216
+        return stack.values();
217
+    }
216 218
 
217 219
     @Override
218 220
     public void setupTopTabsWithViewPager(ViewPager viewPager) {
@@ -230,5 +232,7 @@ public class StackController extends ParentController <StackLayout> {
230 232
     }
231 233
 
232 234
     @RestrictTo(RestrictTo.Scope.TESTS)
233
-    StackLayout getStackLayout() {return getView();}
235
+    StackLayout getStackLayout() {
236
+        return getView();
237
+    }
234 238
 }

+ 3
- 2
lib/android/app/src/main/java/com/reactnativenavigation/views/StackLayout.java View File

@@ -6,6 +6,7 @@ import android.support.annotation.RestrictTo;
6 6
 import android.support.v4.view.ViewPager;
7 7
 import android.widget.RelativeLayout;
8 8
 
9
+import com.reactnativenavigation.interfaces.ChildDisappearListener;
9 10
 import com.reactnativenavigation.parse.Options;
10 11
 import com.reactnativenavigation.presentation.OptionsPresenter;
11 12
 import com.reactnativenavigation.utils.CompatUtils;
@@ -42,8 +43,8 @@ public class StackLayout extends RelativeLayout {
42 43
         optionsPresenter.applyChildOptions(options, child);
43 44
     }
44 45
 
45
-    public void onChildWillDisappear(Options disappearing, Options appearing) {
46
-        new OptionsPresenter(topBar).onChildWillDisappear(disappearing, appearing);
46
+    public void onChildWillDisappear(Options disappearing, Options appearing, ChildDisappearListener childDisappearListener) {
47
+        new OptionsPresenter(topBar).onChildWillDisappear(disappearing, appearing, childDisappearListener);
47 48
     }
48 49
 
49 50
     public void clearOptions() {

+ 25
- 19
lib/android/app/src/main/java/com/reactnativenavigation/views/TopBar.java View File

@@ -12,11 +12,13 @@ import android.support.v7.widget.Toolbar;
12 12
 import android.view.View;
13 13
 import android.widget.TextView;
14 14
 
15
+import com.reactnativenavigation.anim.AnimationListener;
15 16
 import com.reactnativenavigation.anim.TopBarAnimator;
16 17
 import com.reactnativenavigation.anim.TopBarCollapseBehavior;
17 18
 import com.reactnativenavigation.interfaces.ScrollEventListener;
19
+import com.reactnativenavigation.parse.AnimationOptions;
20
+import com.reactnativenavigation.parse.AnimationsOptions;
18 21
 import com.reactnativenavigation.parse.TitleOptions;
19
-import com.reactnativenavigation.parse.params.Bool;
20 22
 import com.reactnativenavigation.parse.params.Button;
21 23
 import com.reactnativenavigation.parse.params.Color;
22 24
 import com.reactnativenavigation.parse.params.Fraction;
@@ -134,26 +136,30 @@ public class TopBar extends AppBarLayout implements ScrollEventListener.ScrollAw
134 136
         collapsingBehavior.disableCollapse();
135 137
     }
136 138
 
137
-    public void show(Bool animated) {
138
-        if (getVisibility() == View.VISIBLE) {
139
-            return;
140
-        }
141
-        if (animated.isTrueOrUndefined()) {
142
-            animator.show();
143
-        } else if (!animator.isRunning()) {
144
-            setVisibility(View.VISIBLE);
145
-        }
139
+    public void show() {
140
+        if (visible()) return;
141
+        setVisibility(View.VISIBLE);
146 142
     }
147 143
 
148
-    public void hide(Bool animated) {
149
-        if (getVisibility() == View.GONE) {
150
-            return;
151
-        }
152
-        if (animated.isTrueOrUndefined()) {
153
-            animator.hide();
154
-        } else if (!animator.isRunning()){
155
-            setVisibility(View.GONE);
156
-        }
144
+    private boolean visible() {
145
+        return getVisibility() == View.VISIBLE;
146
+    }
147
+
148
+    public void showAnimate(AnimationOptions options) {
149
+        if (visible()) return;
150
+        animator.show(options);
151
+    }
152
+
153
+    public void hide() {
154
+        setVisibility(View.GONE);
155
+    }
156
+
157
+    public void hideAnimate(AnimationOptions options) {
158
+        hideAnimate(options, null);
159
+    }
160
+
161
+    public void hideAnimate(AnimationOptions options, AnimationListener listener) {
162
+        animator.hide(options, listener);
157 163
     }
158 164
 
159 165
     @Override

+ 3
- 2
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/StackControllerTest.java View File

@@ -111,7 +111,8 @@ public class StackControllerTest extends BaseTest {
111 111
                 uut.animatePop(new MockPromise() {
112 112
                     @Override
113 113
                     public void resolve(@Nullable Object value) {
114
-                        verify(stackLayout[0], times(1)).onChildWillDisappear(child2.options, child1.options);
114
+                        verify(stackLayout[0], times(1)).onChildWillDisappear(child2.options, child1.options, () -> {
115
+                        });
115 116
                     }
116 117
                 });
117 118
             }
@@ -364,7 +365,7 @@ public class StackControllerTest extends BaseTest {
364 365
         uut.animatePop(new MockPromise() {
365 366
             @Override
366 367
             public void resolve(@Nullable Object value) {
367
-                verify(uut.getTopBar(), times(1)).hide(child2.options.topBarOptions.animate);
368
+                verify(uut.getTopBar(), times(1)).hide();
368 369
             }
369 370
         });
370 371
     }

+ 10
- 9
lib/android/app/src/test/java/com/reactnativenavigation/views/TopBarTest.java View File

@@ -7,9 +7,8 @@ import com.reactnativenavigation.BaseTest;
7 7
 import com.reactnativenavigation.anim.TopBarAnimator;
8 8
 import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
9 9
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
10
-import com.reactnativenavigation.parse.params.Bool;
10
+import com.reactnativenavigation.parse.AnimationOptions;
11 11
 import com.reactnativenavigation.parse.params.Button;
12
-import com.reactnativenavigation.parse.params.NullBool;
13 12
 import com.reactnativenavigation.parse.params.Text;
14 13
 import com.reactnativenavigation.utils.TitleBarHelper;
15 14
 import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
@@ -77,16 +76,18 @@ public class TopBarTest extends BaseTest {
77 76
     }
78 77
 
79 78
     @Test
80
-    public void hide_animateHideUnlessSpecifiedOtherwise() throws Exception {
81
-        uut.hide(new NullBool());
82
-        verify(animator, times(1)).hide();
79
+    public void hide_animate() throws Exception {
80
+        AnimationOptions options = new AnimationOptions();
81
+        uut.hideAnimate(options);
82
+        verify(animator, times(1)).hide(options, null);
83 83
     }
84 84
 
85 85
     @Test
86
-    public void show_animateShowUnlessSpecifiedOtherwise() throws Exception {
87
-        uut.hide(new Bool(false));
88
-        uut.show(new NullBool());
89
-        verify(animator, times(1)).show();
86
+    public void show_animate() throws Exception {
87
+        AnimationOptions options = new AnimationOptions();
88
+        uut.hide();
89
+        uut.showAnimate(options);
90
+        verify(animator, times(1)).show(options);
90 91
     }
91 92
 
92 93
     @Test

+ 52
- 33
playground/src/app.js View File

@@ -26,44 +26,63 @@ function start() {
26 26
     Navigation.setDefaultOptions({
27 27
       _animations: {
28 28
         push: {
29
-          y: {
30
-            from: 1000,
31
-            to: 0,
32
-            duration: 500,
33
-            interpolation: 'decelerate',
34
-          },
35
-          x: {
36
-            from: 1000,
37
-            to: 0,
38
-            duration: 500,
39
-            interpolation: 'decelerate',
40
-            startDelay: 100
29
+          topBar: {
30
+            y: {
31
+              from: 1000,
32
+              to: 0,
33
+              duration: 500,
34
+              interpolation: 'accelerate',
35
+            },
36
+            alpha: {
37
+              from: 0,
38
+              to: 1,
39
+              duration: 500,
40
+              interpolation: 'accelerate'
41
+            }
41 42
           },
42
-          scaleY: {
43
-            from: 0,
44
-            to: 1,
45
-            duration: 1000,
46
-            interpolation: 'decelerate',
43
+          content: {
44
+            y: {
45
+              from: 1000,
46
+              to: 0,
47
+              duration: 500,
48
+              interpolation: 'accelerate',
49
+            },
50
+            alpha: {
51
+              from: 0,
52
+              to: 1,
53
+              duration: 500,
54
+              interpolation: 'accelerate'
55
+            }
47 56
           }
48 57
         },
49 58
         pop: {
50
-          rotationY: {
51
-            from: 0,
52
-            to: -360,
53
-            duration: 500,
54
-            interpolation: 'accelerate',
55
-          },
56
-          x: {
57
-            from: 0,
58
-            to: -1000,
59
-            duration: 500,
60
-            interpolation: 'accelerate',
61
-            startDelay: 100
59
+          topBar: {
60
+            y: {
61
+              from: 0,
62
+              to: 100,
63
+              duration: 500,
64
+              interpolation: 'decelerate',
65
+            },
66
+            alpha: {
67
+              from: 1,
68
+              to: 0,
69
+              duration: 500,
70
+              interpolation: 'decelerate'
71
+            }
62 72
           },
63
-          alpha: {
64
-            from: 1,
65
-            to: 0,
66
-            duration: 500
73
+          content: {
74
+            y: {
75
+              from: 0,
76
+              to: 1000,
77
+              duration: 500,
78
+              interpolation: 'decelerate',
79
+            },
80
+            alpha: {
81
+              from: 1,
82
+              to: 0,
83
+              duration: 500,
84
+              interpolation: 'decelerate'
85
+            }
67 86
           }
68 87
         }
69 88
       }