Browse Source

[V2] Fix for #1880 (#1901)

* animation fix

* fix tests
Roman Kozlov 7 years ago
parent
commit
75ee5f51bc

+ 92
- 36
lib/android/app/src/main/java/com/reactnativenavigation/anim/StackAnimator.java View File

@@ -1,57 +1,113 @@
1 1
 package com.reactnativenavigation.anim;
2 2
 
3
+import android.animation.Animator;
4
+import android.animation.AnimatorSet;
5
+import android.animation.ObjectAnimator;
6
+import android.app.Activity;
3 7
 import android.content.Context;
4
-import android.content.res.TypedArray;
5 8
 import android.support.annotation.Nullable;
9
+import android.util.DisplayMetrics;
6 10
 import android.view.View;
7
-import android.view.animation.Animation;
8
-import android.view.animation.AnimationUtils;
11
+import android.view.WindowManager;
12
+import android.view.animation.AccelerateInterpolator;
13
+import android.view.animation.DecelerateInterpolator;
9 14
 
10 15
 @SuppressWarnings("ResourceType")
11 16
 public class StackAnimator {
12 17
 
13
-	private static int androidOpenEnterAnimResId;
14
-	private static int androidOpenExitAnimResId;
15
-	private static int androidCloseEnterAnimResId;
16
-	private static int androidCloseExitAnimResId;
17
-	private final Context context;
18
+	public interface StackAnimationListener {
19
+		void onAnimationEnd();
20
+	}
21
+
22
+	private static final int DURATION = 300;
23
+	private static final int START_DELAY = 100;
24
+	private static final DecelerateInterpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
25
+	private static final AccelerateInterpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator();
26
+	private float translationY;
18 27
 
19 28
 	public StackAnimator(Context context) {
20
-		this.context = context;
21
-		loadResIfNeeded(context);
29
+		translationY = getWindowHeight(context);
22 30
 	}
23 31
 
24
-	private void loadResIfNeeded(Context context) {
25
-		if (androidOpenEnterAnimResId > 0) return;
32
+	public void animatePush(final View view, @Nullable final StackAnimationListener animationListener) {
33
+		view.setVisibility(View.INVISIBLE);
34
+		ObjectAnimator alpha = ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1);
35
+		alpha.setInterpolator(DECELERATE_INTERPOLATOR);
26 36
 
27
-		int[] attrs = {android.R.attr.activityOpenEnterAnimation, android.R.attr.activityOpenExitAnimation, android.R.attr.activityCloseEnterAnimation, android.R.attr.activityCloseExitAnimation};
28
-		TypedArray typedArray = context.obtainStyledAttributes(android.R.style.Animation_Activity, attrs);
29
-		androidOpenEnterAnimResId = typedArray.getResourceId(0, -1);
30
-		androidOpenExitAnimResId = typedArray.getResourceId(1, -1);
31
-		androidCloseEnterAnimResId = typedArray.getResourceId(2, -1);
32
-		androidCloseExitAnimResId = typedArray.getResourceId(3, -1);
33
-		typedArray.recycle();
34
-	}
37
+		AnimatorSet set = new AnimatorSet();
38
+		set.addListener(new Animator.AnimatorListener() {
39
+			@Override
40
+			public void onAnimationStart(Animator animation) {
41
+				view.setVisibility(View.VISIBLE);
42
+			}
43
+
44
+			@Override
45
+			public void onAnimationEnd(Animator animation) {
46
+				if (animationListener != null) {
47
+					animationListener.onAnimationEnd();
48
+				}
49
+			}
35 50
 
36
-	public void animatePush(final View enteringView, final View exitingView, @Nullable final Runnable onComplete) {
37
-		Animation enterAnim = AnimationUtils.loadAnimation(context, androidOpenEnterAnimResId);
38
-		Animation exitAnim = AnimationUtils.loadAnimation(context, androidOpenExitAnimResId);
51
+			@Override
52
+			public void onAnimationCancel(Animator animation) {
39 53
 
40
-		new ViewAnimationSetBuilder()
41
-				.withEndListener(onComplete)
42
-				.add(enteringView, enterAnim)
43
-				.add(exitingView, exitAnim)
44
-				.start();
54
+			}
55
+
56
+			@Override
57
+			public void onAnimationRepeat(Animator animation) {
58
+
59
+			}
60
+		});
61
+		ObjectAnimator translationY = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, this.translationY, 0);
62
+		translationY.setInterpolator(DECELERATE_INTERPOLATOR);
63
+		translationY.setDuration(DURATION);
64
+		alpha.setDuration(DURATION);
65
+		set.playTogether(translationY, alpha);
66
+		set.start();
45 67
 	}
46 68
 
47
-	public void animatePop(final View enteringView, final View exitingView, @Nullable final Runnable onComplete) {
48
-		Animation enterAnim = AnimationUtils.loadAnimation(context, androidCloseEnterAnimResId);
49
-		Animation exitAnim = AnimationUtils.loadAnimation(context, androidCloseExitAnimResId);
69
+	public void animatePop(View view, @Nullable final StackAnimationListener animationListener) {
70
+		ObjectAnimator alpha = ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0);
71
+		alpha.setInterpolator(ACCELERATE_INTERPOLATOR);
72
+
73
+		AnimatorSet set = new AnimatorSet();
74
+		set.addListener(new Animator.AnimatorListener() {
75
+			@Override
76
+			public void onAnimationStart(Animator animation) {
77
+
78
+			}
50 79
 
51
-		new ViewAnimationSetBuilder()
52
-				.withEndListener(onComplete)
53
-				.add(enteringView, enterAnim)
54
-				.add(exitingView, exitAnim)
55
-				.start();
80
+			@Override
81
+			public void onAnimationEnd(Animator animation) {
82
+				if (animationListener != null) {
83
+					animationListener.onAnimationEnd();
84
+				}
85
+			}
86
+
87
+			@Override
88
+			public void onAnimationCancel(Animator animation) {
89
+
90
+			}
91
+
92
+			@Override
93
+			public void onAnimationRepeat(Animator animation) {
94
+
95
+			}
96
+		});
97
+		ObjectAnimator translationY = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, 0, this.translationY);
98
+		translationY.setInterpolator(ACCELERATE_INTERPOLATOR);
99
+		translationY.setDuration(DURATION);
100
+		alpha.setDuration(DURATION);
101
+		set.playTogether(translationY, alpha);
102
+		set.start();
56 103
 	}
104
+
105
+	private float getWindowHeight(Context context) {
106
+		DisplayMetrics metrics = new DisplayMetrics();
107
+		WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
108
+		windowManager.getDefaultDisplay().getMetrics(metrics);
109
+		return metrics.heightPixels;
110
+	}
111
+
112
+
57 113
 }

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

@@ -1,21 +1,30 @@
1 1
 package com.reactnativenavigation.viewcontrollers;
2 2
 
3
+import android.animation.Animator;
3 4
 import android.app.Activity;
5
+import android.graphics.Color;
6
+import android.os.Handler;
7
+import android.os.Looper;
4 8
 import android.support.annotation.NonNull;
5 9
 import android.view.View;
6 10
 import android.view.ViewGroup;
11
+import android.widget.FrameLayout;
7 12
 import android.widget.LinearLayout;
8 13
 
9 14
 import com.reactnativenavigation.anim.StackAnimator;
15
+import com.reactnativenavigation.react.ReactContainerView;
10 16
 import com.reactnativenavigation.utils.CompatUtils;
11 17
 import com.reactnativenavigation.views.TopBar;
12 18
 
13 19
 import java.util.Collection;
20
+import java.util.Random;
14 21
 
15 22
 public class StackController extends ParentController {
23
+
16 24
 	private final IdStack<ViewController> stack = new IdStack<>();
17 25
 	private final StackAnimator animator;
18 26
 	private TopBar topBar;
27
+	private FrameLayout container;
19 28
 
20 29
 	public StackController(final Activity activity, String id) {
21 30
 		this(activity, id, new StackAnimator(activity));
@@ -31,14 +40,15 @@ public class StackController extends ParentController {
31 40
 
32 41
 		child.setParentStackController(this);
33 42
 		stack.push(child.getId(), child);
43
+		View enteringView = child.getView();
44
+		getContainer().addView(enteringView);
34 45
 
35
-		getView().addView(child.getView());
36
-//TODO animate only when needed
46
+		//TODO animatePush only when needed
37 47
 		if (previousTop != null) {
38
-			animator.animatePush(child.getView(), previousTop.getView(), new Runnable() {
48
+			animator.animatePush(enteringView, new StackAnimator.StackAnimationListener() {
39 49
 				@Override
40
-				public void run() {
41
-					getView().removeView(previousTop.getView());
50
+				public void onAnimationEnd() {
51
+					getContainer().removeView(previousTop.getView());
42 52
 				}
43 53
 			});
44 54
 		}
@@ -54,19 +64,18 @@ public class StackController extends ParentController {
54 64
 		final ViewController poppedTop = stack.pop();
55 65
 		ViewController newTop = peek();
56 66
 
57
-		final View enteringView = newTop.getView();
67
+		View enteringView = newTop.getView();
58 68
 		final View exitingView = poppedTop.getView();
59
-
60
-		getView().addView(enteringView);
61
-
62
-		//TODO animate only when needed
63
-//		animator.animatePop(enteringView, exitingView, new Runnable() {
64
-//			@Override
65
-//			public void run() {
66
-		getView().removeView(exitingView);
67
-		poppedTop.destroy();
68
-//			}
69
-//		});
69
+		getContainer().addView(enteringView, getContainer().getChildCount() - 1);
70
+
71
+		//TODO animatePush only when needed
72
+		animator.animatePop(exitingView, new StackAnimator.StackAnimationListener() {
73
+			@Override
74
+			public void onAnimationEnd() {
75
+				getContainer().removeView(exitingView);
76
+				poppedTop.destroy();
77
+			}
78
+		});
70 79
 	}
71 80
 
72 81
 	public void popSpecific(final ViewController childController) {
@@ -123,6 +132,9 @@ public class StackController extends ParentController {
123 132
 		topBar = new TopBar(getActivity());
124 133
 		topBar.setId(CompatUtils.generateViewId());
125 134
 		root.addView(topBar);
135
+		container = new FrameLayout(getActivity());
136
+		container.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
137
+		root.addView(container);
126 138
 		return root;
127 139
 	}
128 140
 
@@ -136,4 +148,11 @@ public class StackController extends ParentController {
136 148
 		ensureViewIsCreated();
137 149
 		return topBar;
138 150
 	}
151
+
152
+	private ViewGroup getContainer() {
153
+		if (container == null) {
154
+			getView();
155
+		}
156
+		return container;
157
+	}
139 158
 }

+ 4
- 4
lib/android/app/src/test/java/com/reactnativenavigation/mocks/TestStackAnimator.java View File

@@ -14,12 +14,12 @@ public class TestStackAnimator extends StackAnimator {
14 14
 	}
15 15
 
16 16
 	@Override
17
-	public void animatePush(final View enteringView, final View exitingView, final Runnable onComplete) {
18
-		if (onComplete != null) onComplete.run();
17
+	public void animatePush(final View enteringView, StackAnimationListener animationListener) {
18
+		if (animationListener != null) animationListener.onAnimationEnd();
19 19
 	}
20 20
 
21 21
 	@Override
22
-	public void animatePop(final View enteringView, final View exitingView, @Nullable final Runnable onComplete) {
23
-		if (onComplete != null) onComplete.run();
22
+	public void animatePop(final View enteringView, StackAnimationListener animationListener) {
23
+		if (animationListener != null) animationListener.onAnimationEnd();
24 24
 	}
25 25
 }