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
 import android.animation.Animator;
3
 import android.animation.Animator;
4
 import android.animation.AnimatorListenerAdapter;
4
 import android.animation.AnimatorListenerAdapter;
5
 import android.animation.AnimatorSet;
5
 import android.animation.AnimatorSet;
6
-import android.animation.ObjectAnimator;
7
 import android.content.Context;
6
 import android.content.Context;
8
-import android.support.annotation.NonNull;
9
 import android.support.annotation.Nullable;
7
 import android.support.annotation.Nullable;
10
 import android.view.View;
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
 @SuppressWarnings("ResourceType")
10
 @SuppressWarnings("ResourceType")
18
 public class NavigationAnimator extends BaseAnimator {
11
 public class NavigationAnimator extends BaseAnimator {
24
     public void animatePush(final View view, @Nullable final AnimationListener animationListener) {
17
     public void animatePush(final View view, @Nullable final AnimationListener animationListener) {
25
         view.setVisibility(View.INVISIBLE);
18
         view.setVisibility(View.INVISIBLE);
26
         AnimatorSet set;
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
         } else {
22
         } else {
30
             set = getDefaultPushAnimation(view);
23
             set = getDefaultPushAnimation(view);
31
         }
24
         }
47
 
40
 
48
     public void animatePop(View view, @Nullable final AnimationListener animationListener) {
41
     public void animatePop(View view, @Nullable final AnimationListener animationListener) {
49
         AnimatorSet set;
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
         } else {
45
         } else {
53
             set = getDefaultPopAnimation(view);
46
             set = getDefaultPopAnimation(view);
54
         }
47
         }

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

3
 
3
 
4
 import android.animation.Animator;
4
 import android.animation.Animator;
5
 import android.animation.AnimatorListenerAdapter;
5
 import android.animation.AnimatorListenerAdapter;
6
+import android.animation.AnimatorSet;
6
 import android.animation.ObjectAnimator;
7
 import android.animation.ObjectAnimator;
7
 import android.animation.TimeInterpolator;
8
 import android.animation.TimeInterpolator;
8
 import android.view.View;
9
 import android.view.View;
9
-import android.view.ViewGroup;
10
 import android.view.animation.AccelerateInterpolator;
10
 import android.view.animation.AccelerateInterpolator;
11
 import android.view.animation.DecelerateInterpolator;
11
 import android.view.animation.DecelerateInterpolator;
12
 
12
 
13
+import com.reactnativenavigation.parse.AnimationOptions;
13
 import com.reactnativenavigation.views.TopBar;
14
 import com.reactnativenavigation.views.TopBar;
14
 
15
 
15
 public class TopBarAnimator {
16
 public class TopBarAnimator {
16
 
17
 
18
+    private static final int DEFAULT_COLLAPSE_DURATION = 100;
17
     private static final int DURATION_TOPBAR = 300;
19
     private static final int DURATION_TOPBAR = 300;
18
     private final DecelerateInterpolator decelerateInterpolator = new DecelerateInterpolator();
20
     private final DecelerateInterpolator decelerateInterpolator = new DecelerateInterpolator();
19
     private final AccelerateInterpolator accelerateInterpolator = new AccelerateInterpolator();
21
     private final AccelerateInterpolator accelerateInterpolator = new AccelerateInterpolator();
20
 
22
 
21
     private TopBar topBar;
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
     public TopBarAnimator(TopBar topBar) {
27
     public TopBarAnimator(TopBar topBar) {
27
         this.topBar = topBar;
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
         showAnimator.addListener(new AnimatorListenerAdapter() {
46
         showAnimator.addListener(new AnimatorListenerAdapter() {
40
-
41
             @Override
47
             @Override
42
             public void onAnimationStart(Animator animation) {
48
             public void onAnimationStart(Animator animation) {
43
                 topBar.setVisibility(View.VISIBLE);
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
         showAnimator.start();
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
         hideAnimator.addListener(new AnimatorListenerAdapter() {
79
         hideAnimator.addListener(new AnimatorListenerAdapter() {
68
             @Override
80
             @Override
69
             public void onAnimationEnd(Animator animation) {
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
                 topBar.setVisibility(View.GONE);
82
                 topBar.setVisibility(View.GONE);
83
+                if (listener != null) listener.onAnimationEnd();
77
             }
84
             }
78
         });
85
         });
79
         hideAnimator.start();
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
     public boolean isRunning() {
98
     public boolean isRunning() {
83
         return (hideAnimator != null && hideAnimator.isRunning()) || (showAnimator != null && showAnimator.isRunning());
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
 
51
 
52
     @Override
52
     @Override
53
     public void onShow() {
53
     public void onShow() {
54
-        animator.show(topBar.getTranslationY(), null, 100);
54
+        animator.show(topBar.getTranslationY());
55
     }
55
     }
56
 
56
 
57
     @Override
57
     @Override
58
     public void onHide() {
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

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
         AnimationsOptions options = new AnimationsOptions();
11
         AnimationsOptions options = new AnimationsOptions();
12
         if (json == null) return options;
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
         options.startApp = AnimationOptions.parse(json.optJSONObject("startApp"));
16
         options.startApp = AnimationOptions.parse(json.optJSONObject("startApp"));
17
         options.showModal = AnimationOptions.parse(json.optJSONObject("showModal"));
17
         options.showModal = AnimationOptions.parse(json.optJSONObject("showModal"));
18
         options.dismissModal = AnimationOptions.parse(json.optJSONObject("dismissModal"));
18
         options.dismissModal = AnimationOptions.parse(json.optJSONObject("dismissModal"));
20
         return options;
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
     public AnimationOptions startApp = new AnimationOptions();
25
     public AnimationOptions startApp = new AnimationOptions();
26
     public AnimationOptions showModal = new AnimationOptions();
26
     public AnimationOptions showModal = new AnimationOptions();
27
     public AnimationOptions dismissModal = new AnimationOptions();
27
     public AnimationOptions dismissModal = new AnimationOptions();
32
         startApp.mergeWith(other.startApp);
32
         startApp.mergeWith(other.startApp);
33
         showModal.mergeWith(other.showModal);
33
         showModal.mergeWith(other.showModal);
34
         dismissModal.mergeWith(other.dismissModal);
34
         dismissModal.mergeWith(other.dismissModal);
35
-
36
     }
35
     }
37
 
36
 
38
     void mergeWithDefault(AnimationsOptions defaultOptions) {
37
     void mergeWithDefault(AnimationsOptions defaultOptions) {

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

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
 package com.reactnativenavigation.presentation;
1
 package com.reactnativenavigation.presentation;
2
 
2
 
3
 import android.app.Activity;
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
 import com.reactnativenavigation.parse.Options;
8
 import com.reactnativenavigation.parse.Options;
6
 import com.reactnativenavigation.parse.OrientationOptions;
9
 import com.reactnativenavigation.parse.OrientationOptions;
7
 import com.reactnativenavigation.parse.TopBarOptions;
10
 import com.reactnativenavigation.parse.TopBarOptions;
24
     public void applyChildOptions(Options options, Component child) {
27
     public void applyChildOptions(Options options, Component child) {
25
         applyOrientation(options.orientationOptions);
28
         applyOrientation(options.orientationOptions);
26
         applyButtons(options.topBarOptions.leftButtons, options.topBarOptions.rightButtons);
29
         applyButtons(options.topBarOptions.leftButtons, options.topBarOptions.rightButtons);
27
-        applyTopBarOptions(options.topBarOptions, child);
30
+        applyTopBarOptions(options.topBarOptions, child, options.animationsOptions);
28
         applyTopTabsOptions(options.topTabsOptions);
31
         applyTopTabsOptions(options.topTabsOptions);
29
         applyTopTabOptions(options.topTabOptions);
32
         applyTopTabOptions(options.topTabOptions);
30
     }
33
     }
33
         ((Activity) topBar.getContext()).setRequestedOrientation(options.getValue());
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
         if (options.title.text.hasValue()) topBar.setTitle(options.title.text.get());
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
         topBar.setBackgroundColor(options.background.color);
43
         topBar.setBackgroundColor(options.background.color);
40
         topBar.setTitleTextColor(options.title.color);
44
         topBar.setTitleTextColor(options.title.color);
41
         topBar.setTitleFontSize(options.title.fontSize);
45
         topBar.setTitleFontSize(options.title.fontSize);
43
 
47
 
44
         topBar.setTitleTypeface(options.title.fontFamily);
48
         topBar.setTitleTypeface(options.title.fontFamily);
45
         if (options.visible.isFalse()) {
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
         if (options.visible.isTrueOrUndefined()) {
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
         if (options.drawBehind.isTrue()) {
63
         if (options.drawBehind.isTrue()) {
52
             component.drawBehindTopBar();
64
             component.drawBehindTopBar();
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
         if (disappearing.topBarOptions.visible.isTrueOrUndefined() && appearing.topBarOptions.visible.isFalse()) {
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
 
21
 
22
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
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
     private static final NoOpPromise NO_OP = new NoOpPromise();
26
     private static final NoOpPromise NO_OP = new NoOpPromise();
27
     private final IdStack<ViewController> stack = new IdStack<>();
27
     private final IdStack<ViewController> stack = new IdStack<>();
77
     public void animatePush(final ViewController child, final Promise promise) {
77
     public void animatePush(final ViewController child, final Promise promise) {
78
         final ViewController toRemove = stack.peek();
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
         if (toRemove != null) {
85
         if (toRemove != null) {
86
             animator.animatePush(enteringView, () -> {
86
             animator.animatePush(enteringView, () -> {
105
         finishPopping(exitingController.getView(), exitingController, promise);
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
         final ViewController enteringController = stack.peek();
115
         final ViewController enteringController = stack.peek();
116
         popInternal(exitingController, enteringController);
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
     private void popInternal(ViewController disappearing, ViewController appearing) {
122
     private void popInternal(ViewController disappearing, ViewController appearing) {
122
         disappearing.onViewWillDisappear();
123
         disappearing.onViewWillDisappear();
123
         appearing.onViewWillAppear();
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
     boolean canPop() {
130
     boolean canPop() {
129
         return stack.size() > 1;
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
         promise.resolve(poppedTop.getId());
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
             promise.resolve(childController.getId());
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
             } else {
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
             if (animate) {
173
             if (animate) {
172
                 animatePop(promise);
174
                 animatePop(promise);
173
             } else {
175
             } else {
174
                 pop(NO_OP);
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
         return false;
199
         return false;
198
-	}
200
+    }
199
 
201
 
200
     @Override
202
     @Override
201
     public void sendOnNavigationButtonPressed(String buttonId) {
203
     public void sendOnNavigationButtonPressed(String buttonId) {
208
         return new StackLayout(getActivity(), topBarButtonCreator, titleBarReactViewCreator, this::sendOnNavigationButtonPressed);
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
     @Override
219
     @Override
218
     public void setupTopTabsWithViewPager(ViewPager viewPager) {
220
     public void setupTopTabsWithViewPager(ViewPager viewPager) {
230
     }
232
     }
231
 
233
 
232
     @RestrictTo(RestrictTo.Scope.TESTS)
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
 import android.support.v4.view.ViewPager;
6
 import android.support.v4.view.ViewPager;
7
 import android.widget.RelativeLayout;
7
 import android.widget.RelativeLayout;
8
 
8
 
9
+import com.reactnativenavigation.interfaces.ChildDisappearListener;
9
 import com.reactnativenavigation.parse.Options;
10
 import com.reactnativenavigation.parse.Options;
10
 import com.reactnativenavigation.presentation.OptionsPresenter;
11
 import com.reactnativenavigation.presentation.OptionsPresenter;
11
 import com.reactnativenavigation.utils.CompatUtils;
12
 import com.reactnativenavigation.utils.CompatUtils;
42
         optionsPresenter.applyChildOptions(options, child);
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
     public void clearOptions() {
50
     public void clearOptions() {

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

12
 import android.view.View;
12
 import android.view.View;
13
 import android.widget.TextView;
13
 import android.widget.TextView;
14
 
14
 
15
+import com.reactnativenavigation.anim.AnimationListener;
15
 import com.reactnativenavigation.anim.TopBarAnimator;
16
 import com.reactnativenavigation.anim.TopBarAnimator;
16
 import com.reactnativenavigation.anim.TopBarCollapseBehavior;
17
 import com.reactnativenavigation.anim.TopBarCollapseBehavior;
17
 import com.reactnativenavigation.interfaces.ScrollEventListener;
18
 import com.reactnativenavigation.interfaces.ScrollEventListener;
19
+import com.reactnativenavigation.parse.AnimationOptions;
20
+import com.reactnativenavigation.parse.AnimationsOptions;
18
 import com.reactnativenavigation.parse.TitleOptions;
21
 import com.reactnativenavigation.parse.TitleOptions;
19
-import com.reactnativenavigation.parse.params.Bool;
20
 import com.reactnativenavigation.parse.params.Button;
22
 import com.reactnativenavigation.parse.params.Button;
21
 import com.reactnativenavigation.parse.params.Color;
23
 import com.reactnativenavigation.parse.params.Color;
22
 import com.reactnativenavigation.parse.params.Fraction;
24
 import com.reactnativenavigation.parse.params.Fraction;
134
         collapsingBehavior.disableCollapse();
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
     @Override
165
     @Override

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

111
                 uut.animatePop(new MockPromise() {
111
                 uut.animatePop(new MockPromise() {
112
                     @Override
112
                     @Override
113
                     public void resolve(@Nullable Object value) {
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
         uut.animatePop(new MockPromise() {
365
         uut.animatePop(new MockPromise() {
365
             @Override
366
             @Override
366
             public void resolve(@Nullable Object value) {
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
 import com.reactnativenavigation.anim.TopBarAnimator;
7
 import com.reactnativenavigation.anim.TopBarAnimator;
8
 import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
8
 import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
9
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
9
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
10
-import com.reactnativenavigation.parse.params.Bool;
10
+import com.reactnativenavigation.parse.AnimationOptions;
11
 import com.reactnativenavigation.parse.params.Button;
11
 import com.reactnativenavigation.parse.params.Button;
12
-import com.reactnativenavigation.parse.params.NullBool;
13
 import com.reactnativenavigation.parse.params.Text;
12
 import com.reactnativenavigation.parse.params.Text;
14
 import com.reactnativenavigation.utils.TitleBarHelper;
13
 import com.reactnativenavigation.utils.TitleBarHelper;
15
 import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
14
 import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
77
     }
76
     }
78
 
77
 
79
     @Test
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
     @Test
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
     @Test
93
     @Test

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

26
     Navigation.setDefaultOptions({
26
     Navigation.setDefaultOptions({
27
       _animations: {
27
       _animations: {
28
         push: {
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
         pop: {
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
       }