Browse Source

Shared element refactor (#6146)

* Declare animator options once
* Fix <Image> sometimes losing its dimensions during the transition
Guy Carmeli 4 years ago
parent
commit
0b3209bd59
No account linked to committer's email address

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

1
-package com.reactnativenavigation.parse;
2
-
3
-import com.reactnativenavigation.parse.params.Interpolation;
4
-import com.reactnativenavigation.parse.params.NullNumber;
5
-import com.reactnativenavigation.parse.params.NullText;
6
-import com.reactnativenavigation.parse.params.Number;
7
-import com.reactnativenavigation.parse.params.Text;
8
-import com.reactnativenavigation.parse.parsers.InterpolationParser;
9
-import com.reactnativenavigation.parse.parsers.NumberParser;
10
-import com.reactnativenavigation.parse.parsers.TextParser;
11
-
12
-import org.json.JSONObject;
13
-
14
-import androidx.annotation.Nullable;
15
-
16
-public class SharedElementTransitionOptions {
17
-    public Text fromId = new NullText();
18
-    public Text toId = new NullText();
19
-    public Number duration = new NullNumber();
20
-    public Number startDelay = new NullNumber();
21
-    public Interpolation interpolation = Interpolation.NO_VALUE;
22
-
23
-    public static SharedElementTransitionOptions parse(@Nullable JSONObject json) {
24
-        SharedElementTransitionOptions transition = new SharedElementTransitionOptions();
25
-        if (json == null) return transition;
26
-
27
-        transition.fromId = TextParser.parse(json, "fromId");
28
-        transition.toId = TextParser.parse(json, "toId");
29
-        transition.duration = NumberParser.parse(json, "duration");
30
-        transition.startDelay = NumberParser.parse(json, "startDelay");
31
-        transition.interpolation = InterpolationParser.parse(json, "interpolation");
32
-
33
-        return transition;
34
-    }
35
-
36
-    void mergeWith(SharedElementTransitionOptions other) {
37
-        if (other.fromId.hasValue()) fromId = other.fromId;
38
-        if (other.toId.hasValue()) toId = other.toId;
39
-        if (other.duration.hasValue()) duration = other.duration;
40
-        if (other.startDelay.hasValue()) startDelay = other.startDelay;
41
-    }
42
-
43
-    void mergeWithDefault(SharedElementTransitionOptions defaultOptions) {
44
-        if (!fromId.hasValue()) fromId = defaultOptions.fromId;
45
-        if (!toId.hasValue()) toId = defaultOptions.toId;
46
-        if (!duration.hasValue()) duration = defaultOptions.duration;
47
-        if (!startDelay.hasValue()) startDelay = defaultOptions.startDelay;
48
-    }
49
-
50
-    public long getDuration() {
51
-        return duration.get(0).longValue();
52
-    }
53
-
54
-    public long getStartDelay() {
55
-        return startDelay.get(0).longValue();
56
-    }
57
-}

+ 35
- 0
lib/android/app/src/main/java/com/reactnativenavigation/parse/SharedElementTransitionOptions.kt View File

1
+package com.reactnativenavigation.parse
2
+
3
+import android.animation.TimeInterpolator
4
+import com.reactnativenavigation.parse.params.*
5
+import com.reactnativenavigation.parse.params.Number
6
+import com.reactnativenavigation.parse.parsers.InterpolationParser
7
+import com.reactnativenavigation.parse.parsers.NumberParser
8
+import com.reactnativenavigation.parse.parsers.TextParser
9
+import org.json.JSONObject
10
+
11
+class SharedElementTransitionOptions {
12
+    var fromId: Text = NullText()
13
+    var toId: Text = NullText()
14
+    var duration: Number = NullNumber()
15
+    var startDelay: Number = NullNumber()
16
+    var interpolation = Interpolation.NO_VALUE
17
+
18
+    fun getDuration() = duration[0].toLong()
19
+    fun getStartDelay() = startDelay[0].toLong()
20
+    fun getInterpolator(): TimeInterpolator = interpolation.interpolator
21
+
22
+    companion object {
23
+        @JvmStatic
24
+        fun parse(json: JSONObject?): SharedElementTransitionOptions {
25
+            val transition = SharedElementTransitionOptions()
26
+            if (json == null) return transition
27
+            transition.fromId = TextParser.parse(json, "fromId")
28
+            transition.toId = TextParser.parse(json, "toId")
29
+            transition.duration = NumberParser.parse(json, "duration")
30
+            transition.startDelay = NumberParser.parse(json, "startDelay")
31
+            transition.interpolation = InterpolationParser.parse(json, "interpolation")
32
+            return transition
33
+        }
34
+    }
35
+}

+ 0
- 19
lib/android/app/src/main/java/com/reactnativenavigation/utils/Animator.kt View File

1
-package com.reactnativenavigation.utils
2
-
3
-import android.animation.Animator
4
-import android.animation.TimeInterpolator
5
-
6
-fun Animator.withStartDelay(delay: Long): Animator {
7
-    startDelay = delay
8
-    return this
9
-}
10
-
11
-fun Animator.withDuration(duration: Long): Animator {
12
-    this.duration = duration
13
-    return this
14
-}
15
-
16
-fun Animator.withInterpolator(interpolator: TimeInterpolator): Animator {
17
-    this.interpolator = interpolator
18
-    return this
19
-}

+ 5
- 1
lib/android/app/src/main/java/com/reactnativenavigation/views/element/SharedElementTransition.kt View File

22
     override fun createAnimators(): AnimatorSet {
22
     override fun createAnimators(): AnimatorSet {
23
         val animators = animators()
23
         val animators = animators()
24
                 .filter { it.shouldAnimateProperty() }
24
                 .filter { it.shouldAnimateProperty() }
25
-                .map { it.create(options) }
25
+                .map { it.create(options).apply {
26
+                    duration = options.getDuration()
27
+                    startDelay = options.getStartDelay()
28
+                    interpolator = options.getInterpolator()
29
+                } }
26
         val set = AnimatorSet()
30
         val set = AnimatorSet()
27
         set.playTogether(animators)
31
         set.playTogether(animators)
28
         return set
32
         return set

+ 2
- 4
lib/android/app/src/main/java/com/reactnativenavigation/views/element/TransitionAnimatorCreator.kt View File

116
             val lp = FrameLayout.LayoutParams(view.layoutParams)
116
             val lp = FrameLayout.LayoutParams(view.layoutParams)
117
             lp.topMargin = loc.y
117
             lp.topMargin = loc.y
118
             lp.leftMargin = loc.x
118
             lp.leftMargin = loc.x
119
-            if (view !is ReactImageView) {
120
-                lp.width = view.width
121
-                lp.height = view.height
122
-            }
119
+            lp.width = view.width
120
+            lp.height = view.height
123
             view.layoutParams = lp
121
             view.layoutParams = lp
124
             transition.viewController.requireParentController().addOverlay(view)
122
             transition.viewController.requireParentController().addOverlay(view)
125
         }
123
         }

+ 1
- 9
lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/BackgroundColorAnimator.kt View File

26
         val toColor = ColorUtils.colorToLAB(ViewUtils.getBackgroundColor(to))
26
         val toColor = ColorUtils.colorToLAB(ViewUtils.getBackgroundColor(to))
27
 
27
 
28
         backgroundColorEvaluator.evaluate(0f, fromColor, toColor)
28
         backgroundColorEvaluator.evaluate(0f, fromColor, toColor)
29
-        return ObjectAnimator
30
-                .ofObject(
31
-                        backgroundColorEvaluator,
32
-                        fromColor,
33
-                        toColor
34
-                )
35
-                .withDuration(options.getDuration())
36
-                .withStartDelay(options.getStartDelay())
37
-                .withInterpolator(options.interpolation.interpolator)
29
+        return ObjectAnimator.ofObject(backgroundColorEvaluator, fromColor, toColor)
38
     }
30
     }
39
 }
31
 }

+ 7
- 14
lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/MatrixAnimator.kt View File

9
 import com.facebook.react.views.image.ReactImageView
9
 import com.facebook.react.views.image.ReactImageView
10
 import com.reactnativenavigation.parse.SharedElementTransitionOptions
10
 import com.reactnativenavigation.parse.SharedElementTransitionOptions
11
 import com.reactnativenavigation.utils.ViewUtils
11
 import com.reactnativenavigation.utils.ViewUtils
12
-import com.reactnativenavigation.utils.withDuration
13
-import com.reactnativenavigation.utils.withInterpolator
14
-import com.reactnativenavigation.utils.withStartDelay
15
 
12
 
16
 class MatrixAnimator(from: View, to: View) : PropertyAnimatorCreator<ReactImageView>(from, to) {
13
 class MatrixAnimator(from: View, to: View) : PropertyAnimatorCreator<ReactImageView>(from, to) {
17
     override fun shouldAnimateProperty(fromChild: ReactImageView, toChild: ReactImageView): Boolean {
14
     override fun shouldAnimateProperty(fromChild: ReactImageView, toChild: ReactImageView): Boolean {
27
                     calculateBounds(to)
24
                     calculateBounds(to)
28
             )
25
             )
29
 
26
 
30
-            return ObjectAnimator
31
-                    .ofObject(TypeEvaluator<Float> { fraction: Float, _: Any, _: Any ->
32
-                        hierarchy.actualImageScaleType?.let {
33
-                            (hierarchy.actualImageScaleType as InterpolatingScaleType?)!!.value = fraction
34
-                            to.invalidate()
35
-                        }
36
-                        null
37
-                    }, 0, 1)
38
-                    .withDuration(options.getDuration())
39
-                    .withStartDelay(options.getStartDelay())
40
-                    .withInterpolator(options.interpolation.interpolator)
27
+            return ObjectAnimator.ofObject(TypeEvaluator<Float> { fraction: Float, _: Any, _: Any ->
28
+                hierarchy.actualImageScaleType?.let {
29
+                    (hierarchy.actualImageScaleType as InterpolatingScaleType?)!!.value = fraction
30
+                    to.invalidate()
31
+                }
32
+                null
33
+            }, 0, 1)
41
         }
34
         }
42
     }
35
     }
43
 
36
 

+ 1
- 8
lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/RotationAnimator.kt View File

5
 import android.view.View
5
 import android.view.View
6
 import com.facebook.react.views.image.ReactImageView
6
 import com.facebook.react.views.image.ReactImageView
7
 import com.reactnativenavigation.parse.SharedElementTransitionOptions
7
 import com.reactnativenavigation.parse.SharedElementTransitionOptions
8
-import com.reactnativenavigation.utils.withDuration
9
-import com.reactnativenavigation.utils.withInterpolator
10
-import com.reactnativenavigation.utils.withStartDelay
11
 
8
 
12
 class RotationAnimator(from: View, to: View) : PropertyAnimatorCreator<ReactImageView>(from, to) {
9
 class RotationAnimator(from: View, to: View) : PropertyAnimatorCreator<ReactImageView>(from, to) {
13
     private val fromRotation = from.rotation
10
     private val fromRotation = from.rotation
21
         to.rotation = fromRotation
18
         to.rotation = fromRotation
22
         to.pivotX = 0f
19
         to.pivotX = 0f
23
         to.pivotY = 0f
20
         to.pivotY = 0f
24
-        return ObjectAnimator
25
-                .ofFloat(to, View.ROTATION, fromRotation, toRotation)
26
-                .withDuration(options.getDuration())
27
-                .withStartDelay(options.getStartDelay())
28
-                .withInterpolator(options.interpolation.interpolator)
21
+        return ObjectAnimator.ofFloat(to, View.ROTATION, fromRotation, toRotation)
29
     }
22
     }
30
 }
23
 }

+ 1
- 10
lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/ScaleXAnimator.kt View File

4
 import android.animation.ObjectAnimator
4
 import android.animation.ObjectAnimator
5
 import android.view.View
5
 import android.view.View
6
 import android.view.ViewGroup
6
 import android.view.ViewGroup
7
-import androidx.core.animation.addListener
8
-import androidx.core.animation.doOnStart
9
 import com.facebook.react.views.text.ReactTextView
7
 import com.facebook.react.views.text.ReactTextView
10
 import com.reactnativenavigation.parse.SharedElementTransitionOptions
8
 import com.reactnativenavigation.parse.SharedElementTransitionOptions
11
-import com.reactnativenavigation.utils.withDuration
12
-import com.reactnativenavigation.utils.withInterpolator
13
-import com.reactnativenavigation.utils.withStartDelay
14
 
9
 
15
 class ScaleXAnimator(from: View, to: View) : PropertyAnimatorCreator<ViewGroup>(from, to) {
10
 class ScaleXAnimator(from: View, to: View) : PropertyAnimatorCreator<ViewGroup>(from, to) {
16
     override fun shouldAnimateProperty(fromChild: ViewGroup, toChild: ViewGroup): Boolean {
11
     override fun shouldAnimateProperty(fromChild: ViewGroup, toChild: ViewGroup): Boolean {
21
 
16
 
22
     override fun create(options: SharedElementTransitionOptions): Animator {
17
     override fun create(options: SharedElementTransitionOptions): Animator {
23
         to.scaleX = from.width.toFloat() / to.width
18
         to.scaleX = from.width.toFloat() / to.width
24
-        return ObjectAnimator
25
-                .ofFloat(to, View.SCALE_X, from.width.toFloat() / to.width, 1f)
26
-                .withDuration(options.getDuration())
27
-                .withStartDelay(options.getStartDelay())
28
-                .withInterpolator(options.interpolation.interpolator)
19
+        return ObjectAnimator.ofFloat(to, View.SCALE_X, from.width.toFloat() / to.width, 1f)
29
     }
20
     }
30
 }
21
 }

+ 1
- 8
lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/ScaleYAnimator.kt View File

6
 import android.view.ViewGroup
6
 import android.view.ViewGroup
7
 import com.facebook.react.views.text.ReactTextView
7
 import com.facebook.react.views.text.ReactTextView
8
 import com.reactnativenavigation.parse.SharedElementTransitionOptions
8
 import com.reactnativenavigation.parse.SharedElementTransitionOptions
9
-import com.reactnativenavigation.utils.withDuration
10
-import com.reactnativenavigation.utils.withInterpolator
11
-import com.reactnativenavigation.utils.withStartDelay
12
 
9
 
13
 class ScaleYAnimator(from: View, to: View) : PropertyAnimatorCreator<ViewGroup>(from, to) {
10
 class ScaleYAnimator(from: View, to: View) : PropertyAnimatorCreator<ViewGroup>(from, to) {
14
     override fun shouldAnimateProperty(fromChild: ViewGroup, toChild: ViewGroup): Boolean {
11
     override fun shouldAnimateProperty(fromChild: ViewGroup, toChild: ViewGroup): Boolean {
19
 
16
 
20
     override fun create(options: SharedElementTransitionOptions): Animator {
17
     override fun create(options: SharedElementTransitionOptions): Animator {
21
         to.scaleY = from.height.toFloat() / to.height
18
         to.scaleY = from.height.toFloat() / to.height
22
-        return ObjectAnimator
23
-                .ofFloat(to, View.SCALE_Y, from.height.toFloat() / to.height, 1f)
24
-                .withDuration(options.getDuration())
25
-                .withStartDelay(options.getStartDelay())
26
-                .withInterpolator(options.interpolation.interpolator)
19
+        return ObjectAnimator.ofFloat(to, View.SCALE_Y, from.height.toFloat() / to.height, 1f)
27
     }
20
     }
28
 }
21
 }

+ 1
- 4
lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/TextChangeAnimator.kt View File

35
                 }
35
                 }
36
                 .setTextColorGetter {
36
                 .setTextColorGetter {
37
                     TextViewUtils.getTextColor(it)
37
                     TextViewUtils.getTextColor(it)
38
-                }
39
-                .buildAnimator()
40
-                .setDuration(options.getDuration())
41
-                .withInterpolator(options.interpolation.interpolator)
38
+                }.buildAnimator()
42
     }
39
     }
43
 }
40
 }

+ 1
- 12
lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/XAnimator.kt View File

1
 package com.reactnativenavigation.views.element.animators
1
 package com.reactnativenavigation.views.element.animators
2
 
2
 
3
 import android.animation.Animator
3
 import android.animation.Animator
4
-import android.animation.AnimatorListenerAdapter
5
 import android.animation.ObjectAnimator
4
 import android.animation.ObjectAnimator
6
 import android.view.View
5
 import android.view.View
7
 import android.view.View.TRANSLATION_X
6
 import android.view.View.TRANSLATION_X
8
-import androidx.core.animation.addListener
9
-import androidx.core.animation.doOnStart
10
-import com.facebook.react.views.image.ReactImageView
11
 import com.facebook.react.views.text.ReactTextView
7
 import com.facebook.react.views.text.ReactTextView
12
 import com.reactnativenavigation.parse.SharedElementTransitionOptions
8
 import com.reactnativenavigation.parse.SharedElementTransitionOptions
13
 import com.reactnativenavigation.utils.ViewUtils
9
 import com.reactnativenavigation.utils.ViewUtils
14
-import com.reactnativenavigation.utils.withDuration
15
-import com.reactnativenavigation.utils.withInterpolator
16
-import com.reactnativenavigation.utils.withStartDelay
17
 
10
 
18
 class XAnimator(from: View, to: View) : PropertyAnimatorCreator<View>(from, to) {
11
 class XAnimator(from: View, to: View) : PropertyAnimatorCreator<View>(from, to) {
19
     private val dx: Int
12
     private val dx: Int
31
     override fun create(options: SharedElementTransitionOptions): Animator {
24
     override fun create(options: SharedElementTransitionOptions): Animator {
32
         to.translationX = dx.toFloat()
25
         to.translationX = dx.toFloat()
33
         to.pivotX = 0f
26
         to.pivotX = 0f
34
-        return ObjectAnimator
35
-                .ofFloat(to, TRANSLATION_X, dx.toFloat(), 0f)
36
-                .withDuration(options.getDuration())
37
-                .withStartDelay(options.getStartDelay())
38
-                .withInterpolator(options.interpolation.interpolator)
27
+        return ObjectAnimator.ofFloat(to, TRANSLATION_X, dx.toFloat(), 0f)
39
     }
28
     }
40
 }
29
 }

+ 1
- 9
lib/android/app/src/main/java/com/reactnativenavigation/views/element/animators/YAnimator.kt View File

8
 import com.facebook.react.views.text.ReactTextView
8
 import com.facebook.react.views.text.ReactTextView
9
 import com.reactnativenavigation.parse.SharedElementTransitionOptions
9
 import com.reactnativenavigation.parse.SharedElementTransitionOptions
10
 import com.reactnativenavigation.utils.ViewUtils
10
 import com.reactnativenavigation.utils.ViewUtils
11
-import com.reactnativenavigation.utils.withDuration
12
-
13
-import com.reactnativenavigation.utils.withInterpolator
14
-import com.reactnativenavigation.utils.withStartDelay
15
 
11
 
16
 class YAnimator(from: View, to: View) : PropertyAnimatorCreator<View>(from, to) {
12
 class YAnimator(from: View, to: View) : PropertyAnimatorCreator<View>(from, to) {
17
     private val dy: Int
13
     private val dy: Int
29
     override fun create(options: SharedElementTransitionOptions): Animator {
25
     override fun create(options: SharedElementTransitionOptions): Animator {
30
         to.translationY = dy.toFloat()
26
         to.translationY = dy.toFloat()
31
         to.pivotY = 0f
27
         to.pivotY = 0f
32
-        return ObjectAnimator
33
-                .ofFloat(to, TRANSLATION_Y, dy.toFloat(), 0f)
34
-                .withDuration(options.getDuration())
35
-                .withStartDelay(options.getStartDelay())
36
-                .withInterpolator(options.interpolation.interpolator)
28
+        return ObjectAnimator.ofFloat(to, TRANSLATION_Y, dy.toFloat(), 0f)
37
     }
29
     }
38
 }
30
 }