自定义Transition
Android Transition框架提供了丰富的内建Transition,一般情况下能够满足我们的需求。但是有些时候仅仅依靠内建Transition已经不能满足射击师的狂想,此时我们需要自己实现一些满足特殊场景的Transition。
一个自定义的Transition同内建的Transition实现方式一致,创建一个类继承自Transition类。该类需要处理的逻辑主要有以下三点:
- 开始场景和结束场景的发生变化的对应view的属性;
- 依据开始场景view的属性值和结束场景view的属性值创建属性动画;
- 指定该Transition的target views。
继承Transition类
Transition类是一个抽象类,一个自定义Transition一般只需要重写三个方法,如下:
class CustomTransition : Transition {
constructor() : super()
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
override fun captureStartValues(startValues: TransitionValues?) {
}
override fun captureEndValues(endValues: TransitionValues?) {
}
override fun createAnimator(sceneRoot: ViewGroup?, startValues: TransitionValues?, endValues: TransitionValues?): Animator? {
return null
}
}
两个构造方法
transition类有两个构造方法,无参构造器只能用于code方式构造transition对象实例。但是如果要在XML文件中使用自定义Transition,则必须实现constructor(context: Context?, attrs: AttributeSet?)
构造器。使用方式:
<transition class="com.iyao.transition.ChangeRectRadiusAndColor"/>
Transition框架使用属性动画实现过渡动画。因此,Transition类需要属性的开始值和结束值来构建动画。然而,一个属性动画通常只需要一个view的全部属性的一部分。一旦一个Transiton动画所需要的属性被指定,那么Transition类没必要再关注view的其他属性。因此,Transition框架提供回调方法用于捕获该Transition关注的属性。其中fun captureStartValues(transitionValues: TransitionValues)
和fun captureEndValues(transitionValues: TransitionValues)
这两个方法分别用于获取开始和结束的view属性,然后保存到TransitionValues对象的values中,用于fun createAnimator(sceneRoot: ViewGroup, startValues: TransitionValues?, endValues: TransitionValues?): Animator?
方法创建属性动画。
captureStartValues方法捕获startingScene属性值集合
Transition框架会采用深度优先遍历方法遍历startingScene的所有views。并在递归过程中为每一个view调用captureStartValues()
方法,该方法的参数TransitionValues对象持有该view的引用和一个Map实例用于保存过渡动画需要的属性值。在这个方法中,我们只要获取该view实现动画所需要的属性值,然后保存到Map对象中,Transition框架会把TransitionValues对象保存起来,用于创建动画。
captureEndValues方法捕获endingScene属性值集合
captureEndValues()
方法和captureStartValues()
方法作用一致,不过遍历的views是endingScene中的。
createAnimator方法构建属性动画
当Transition框架捕获到用于构建动画的属性集合后,会调用Transition类的createAnimator()
方法获取一个Animator对象。该方法默认实现返回一个null,Transition子类需要重写该方法以构建自己的属性动画。
方法参数中,sceneRoot是TransitionManager.go()
方法传入的Scene的SceneRoot,startValues和endValues是captureStartValues()
方法和captureEndValues()
方法传入的TransitionValues对象。
示例代码:
package com.iyao.transition
import ArgbEvaluator
import android.animation.Animator
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.os.Build
import android.transition.Transition
import android.transition.TransitionValues
import android.view.ViewGroup
class ChangeRectRadiusAndColor : Transition() {
//官方建议属性键名命名规则:package:class:property
private val propertyRadius = "com.iyao.transition:RectView:radius"
private val propertyColor = "com.iyao.transition:RectView:color"
//init {
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// addTarget(RectView::class.java)
// }
//}
override fun captureEndValues(transitionValues: TransitionValues) {
captureValues(transitionValues)
}
override fun captureStartValues(transitionValues: TransitionValues) {
captureValues(transitionValues)
}
private fun captureValues(transitionValues: TransitionValues) {
if (transitionValues.view is RectView) {
val rectView = transitionValues.view as RectView
transitionValues.values.put(propertyRadius, rectView.radius)
transitionValues.values.put(propertyColor, rectView.color)
}
}
override fun createAnimator(sceneRoot: ViewGroup, startValues: TransitionValues?, endValues: TransitionValues?): Animator? {
return when {
startValues?.view is RectView && endValues?.view is RectView -> {
val startColor: Int = startValues.values[propertyColor] as Int
val endColor: Int = endValues.values[propertyColor] as Int
val startRadius = startValues.values[propertyRadius] as Float
val endRadius = endValues.values[propertyRadius] as Float
val rectView = endValues.view as RectView
var radiusAnimator : Animator? = null
var argbAnimator : Animator? = null
if (startRadius != endRadius) {
rectView.radius = startRadius
radiusAnimator = createRadiusAnimator(rectView, startRadius, endRadius)
}
if (startColor != endColor) {
rectView.color = startColor
argbAnimator = createArgbAnimator(rectView, startColor, endColor)
}
when {
radiusAnimator != null && argbAnimator != null -> {
AnimatorSet().apply {
playTogether(radiusAnimator, argbAnimator)
}
}
else -> radiusAnimator ?: argbAnimator
}
}
else -> null
}
}
private fun createRadiusAnimator(target: Any, vararg radius : Float): Animator
= ObjectAnimator.ofFloat(target, "radius", *radius)
private fun createArgbAnimator(target: Any, vararg colors: Int): Animator {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
ObjectAnimator.ofArgb(target, "color", *colors)
} else {
ObjectAnimator.ofInt(target, "color", *colors).apply {
setEvaluator(ArgbEvaluator.instance)
}
}
}
}
效果图:
完整代码github
上一篇:Android过渡动画Scene and Transition(一):使用Transition框架实现场景过渡动画
网友评论