一句话描述:
使用 Kotlin DSL (Domain-Specific Language,领域特定语言)和 ValueAnimator 实现一个动画框架,以 DSL 的方式来方便、快速的实现对 view 的各种属性动画。
通过 Kotlin DSL 的方式来组合针对 view 的多个属性的动画,用于快速、简单的实现复杂的组合动画效果。简单又好用——使用示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 fun animateTest (view: View ) { val controller: Animators.Controller = quickAnimate { together { play { targets = listOf(view) duration = 300L interpolator = LinearInterpolator() translationX(0f , 100f ) } play { targets = listOf(view) duration = 300L interpolator = LinearInterpolator() translationY(0f , 100f ) } } onStart = {} onEnd = {} } controller.start() }
Animators 类: quickAnimate { } 代码块中的 receiver 实现类。通过 封装 Controller 的方式对外提供控制动画的接口。
1 fun quickAnimate (init : Animators .() -> Unit ) : Animators.Controller
SingleAnimator 类: 具体的 动画动作 封装类 ,也就是 play { } 代码块中的receiver 实现类。负责完成单个属性动画。
1 fun play (anim: SingleAnimator .() -> Unit )
同时也支持多个动画的组合动画,通过 together 包裹的 动画会同时执行。否则,默认所有动画都是 串行执行。
1 fun together (init : Animators .() -> Unit )
最后,提供一个控制类 Animators.Controller ,用于 控制 动画的 开始 、取消,暂停 等操作。
API 接口设计如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 fun quickAnimate (init : Animators .() -> Unit ) : Animators.Controllerclass Animators { class Controller { fun start () fun cancel () fun pause () fun resume () fun isRunning () : Boolean } fun play (anim: SingleAnimator .() -> Unit ) fun together (init : Animators .() -> Unit ) class SingleAnimator { } }
对于 具体的View 的属性动画,主要利用以下API 来实现:
1 public static <T> ObjectAnimator ofFloat(T target, Property<T, Float > property, float... values)
ObjectAnimator
是 Android 动画框架中的一个类,专门用于执行属性动画。ObjectAnimator
可以对目标对象的某个属性执行动画,例如透明度、旋转角度、位置等。
Property<T, Float> property
: 目标对象的属性。Property
是一个属性的抽象表示,这里可以传入以下属性:
1 2 3 4 5 View.ALPHA View.ROTATION View.TRANSLATION_X View.TRANSLATION_Y ......
float... values
: 动画的关键帧值序列。可以传入多个值,动画将依次在这些值之间进行插值。
对于单个动画,可以提供以下配置属性,用于配置动画的 view对象、动画时长、插值器、repeatMode 等参数。
1 2 3 4 5 var duration: Long = 0L var targets: List<View?> = emptyList()var interpolator: TimeInterpolator? = null var repeatMode: Int = ValueAnimator.RESTARTvar repeatCount: Int = 0
单个动画使用示例:
1 2 3 4 5 6 7 8 play { targets = listOf(view) duration = 300L interpolator = LinearInterpolator() repeatMode = ValueAnimator.RESTART repeatCount = 0 translationY(0f , 100f ) }
注意:这里只是完成了动画的配置,并没有真正开始动画,需要通过 Animators.Controller 来统一来控制动画的 开始 、暂停、继续、取消等。
完整源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 package com.example.helloworldimport android.animation.*import android.util.Logimport android.util.Propertyimport android.view.Viewimport android.view.animation.LinearInterpolatorfun quickAnimate (init : Animators .() -> Unit ) : Animators.Controller { val animators = Animators().apply(init ) val animSet = AnimatorSet() animSet.addListener(object : Animator.AnimatorListener { override fun onAnimationStart (animation: Animator ?) { animators.onStart?.invoke() } override fun onAnimationEnd (animation: Animator ?) { animators.onEnd?.invoke() } override fun onAnimationCancel (animation: Animator ?) { } override fun onAnimationRepeat (animation: Animator ?) { } }) Log.i("xcc" , "quick anim: ${animators.getAnimatorList()} " ) animSet.playSequentially(animators.getAnimatorList()) return Animators.Controller(animSet) }class Animators { var onStart: (() -> Unit )? = null var onEnd: (() -> Unit )? = null private val anims: MutableList<Animator> = mutableListOf() fun getAnimatorList () = anims.toList() fun play (anim: SingleAnimator .() -> Unit ) { val animator = SingleAnimator().apply(anim).createAnimator() Log.i("xcc" , "add anim: $animator " ) anims.add(animator) } fun together (init : Animators .() -> Unit ) { val animSet = Animators().apply(init ).createAnimator() Log.i("xcc" , "add animSet: $animSet " ) anims.add(animSet) } private fun createAnimator () : Animator { val animSet = AnimatorSet() animSet.addListener(object : Animator.AnimatorListener { override fun onAnimationStart (animation: Animator ?) { onStart?.invoke() } override fun onAnimationEnd (animation: Animator ?) { onEnd?.invoke() } override fun onAnimationCancel (animation: Animator ?) { } override fun onAnimationRepeat (animation: Animator ?) { } }) animSet.playTogether(anims.toList()) return animSet } class Controller (private val animator: Animator) { fun start () { animator.start() } fun cancel () { animator.cancel() } fun pause () { animator.pause() } fun resume () { animator.resume() } fun isRunning () : Boolean { return animator.isRunning } } class SingleAnimator { var duration: Long = 0L var targets: List<View?> = emptyList() var interpolator: TimeInterpolator? = null var repeatMode: Int = ValueAnimator.RESTART var repeatCount: Int = 0 private val animList: MutableList<Animator> = mutableListOf() fun createAnimator () : Animator { val animSet = AnimatorSet() animSet.duration = this .duration animSet.interpolator = this .interpolator animSet.playSequentially(animList.toList()) return animSet } fun translationY ( vararg values: Float , onUpdate: ((animation : ValueAnimator ) -> Unit )? = null ) { anim(property = View.TRANSLATION_Y, values = values, onUpdate = onUpdate) } fun translationX ( vararg values: Float , onUpdate: ((animation : ValueAnimator ) -> Unit )? = null ) { anim(property = View.TRANSLATION_X, values = values, onUpdate = onUpdate) } fun alpha ( vararg values: Float , onUpdate: ((animation : ValueAnimator ) -> Unit )? = null ) { anim(property = View.ALPHA, values = values, onUpdate = onUpdate) } fun rotate ( vararg values: Float , onUpdate: ((animation : ValueAnimator ) -> Unit )? = null ) { anim(property = View.ROTATION, values = values, onUpdate = onUpdate) } private fun anim ( property: Property <View , Float >, vararg values: Float , onUpdate: ((animation : ValueAnimator ) -> Unit )? = null ) { targets.forEach { if (it == null ) { return @forEach } val anim = ObjectAnimator.ofFloat(it, property, *values) anim.addUpdateListener { animation -> onUpdate?.invoke(animation) } anim.duration = this .duration anim.interpolator = this .interpolator anim.repeatMode = this .repeatMode anim.repeatCount = this .repeatCount animList.add(anim) } } } }var controller: Animators.Controller? = null fun animateTest (view: View ) { controller = quickAnimate { together { play { targets = listOf(view) duration = 300L interpolator = LinearInterpolator() translationX(view.translationX, view.translationX + 100f ) } play { targets = listOf(view) duration = 300L interpolator = LinearInterpolator() translationY(view.translationY, view.translationY + 100f ) { Log.i("xcc" , "on update: ${it.animatedValue} " ) } } } play { targets = listOf(view) duration = 300L interpolator = LinearInterpolator() alpha(0f , 1f ) } play { targets = listOf(view) duration = 300L interpolator = LinearInterpolator() rotate(0f , 360f ) } onStart = { Log.i("xcc" , "onStart" ) } onEnd = { Log.i("xcc" , "onEnd" ) } } controller?.start() }