属性动画
属性动画是通过直接改变View属性,实现的动画效果。与补间动画不同的是,属性动画是对象的属性的真实改变,而补间动画仅仅是个动画效果,实际属性没有改变。直观表现是属性动画的点击事件会随着动画的过程有范围性的改变。比如你想做个幸运大轮盘的动画,那你得用属性动画而不是补间动画。
属性动画中支持对int,float,rbg等属性的支持,安卓给出了他们自定义的估值函数。用于记录动画过程中属性的改变。具体手段是用过实现TypeEvaluator接口,自定义各种属性的evaluate方法。一般情况下,如果你的动画效果比较简单,那么使用sdk中提供的几种估值器就够用了,但是对于比较复杂的场景,需要自己自定义估值函数。
估值器是什么
估值器是属性动画中的进阶手段,对于一些sdk中没有预定义的对象给出自己的实现。如果除了属性之外还有其他对象或者自定义的一些属性来控制动画,则需要自己实现估值器。
估值器怎么用
使用SDK中自带的Float估值器实现简单的旋转、透明度、缩放效果,下面是代码
// 0f,1f,0f表示属性的变化过程,具体的变化受FloatEvaluator()的evaluate方法控制
// 提前剧透一下,FloatEvaluator中的方法是匀速变化的,所以拿到的animatedValue
// 先从0变为1再变为0
val anim = ValueAnimator.ofObject(FloatEvaluator(), 0f, 1f, 0f).apply {
// 定义了整个动画的时间为1000ms
duration = 1000
addUpdateListener {
// 透明度从 1 变为 0.5 再变为 1
view_anim.alpha = (2 - it.animatedValue as Float) / 2
// 旋转角度从 0 变为 180 再变为 0
view_anim.rotation = it.animatedValue as Float * 180
// 尺度缩放从 1 变为 0.5 再变为 1
view_anim.scaleX = (2 - it.animatedValue as Float) / 2
view_anim.scaleY = (2 - it.animatedValue as Float) / 2
}
}
下面是效果:
估值器的简单使用
再看下FloatEvaluator的evaluate方法:
/**
* This function returns the result of linearly interpolating the start and end values, with
* <code>fraction</code> representing the proportion between the start and end values. The
* calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
* where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
* and <code>t</code> is <code>fraction</code>.
*
* @param fraction The fraction from the starting to the ending values
* @param startValue The start value; should be of type <code>float</code> or
* <code>Float</code>
* @param endValue The end value; should be of type <code>float</code> or <code>Float</code>
* @return A linear interpolation between the start and end values, given the
* <code>fraction</code> parameter.
*/
public Float evaluate(float fraction, Number startValue, Number endValue) {
float startFloat = startValue.floatValue();
return startFloat + fraction * (endValue.floatValue() - startFloat);
}
fraction表示每个阶段动画完成的进度 0~1
startValue和endValue表示每一个阶段的开始和结束值
举个例子,0f,1f,0f,1f这个变化过程
0f-1f阶段
startValue一直为0f,endValue一直为1f,fraction从0变为1
1f-0f阶段
startValue一直为1f,endValue一直为0f,fraction从0变为1
0f-1f阶段
startValue一直为0f,endValue一直为1f,fraction从0变为1
而这一切都是在1000ms中完成的。
为了方便理解,将这个变化过程画成图像,其中横坐标代表时间,纵坐标表示fraction,startValue和endValue的值:
根据上述变化过程的分析,可以得到evaluate方法的返回值的变化图像:
startFloat + fraction * (endValue.floatValue() - startFloat)
evaluate方法返回值随时间变化图像
将返回值直接用于平移属性:
val anim = ValueAnimator.ofObject(FloatEvaluator(), 0f, 1f, 0f, 1f).apply {
duration = 1000
addUpdateListener {
view_anim.translationX = it.animatedValue as Float * 100
}
}
效果是这样的,可以看到,变化过程与我们画的函数图像完全相符。
使用估值器实现小球下落动画
经过上诉的分析,我们已经基本了解了估值器的原理,是时候创建自己的估值器了。我们可以使用估值器模拟一个小球下落的过程。
代码如下:
class AnimFragment4 : BaseFragment() {
override fun getLayoutId() = R.layout.fragment_anim4
override fun initViews() {
// 裁剪为圆球形
view_anim?.outlineProvider = object : ViewOutlineProvider() {
override fun getOutline(view: View?, outline: Outline?) {
outline?.setOval(0, 0, view_anim?.width ?: 0, view_anim?.height ?: 0);
}
}
view_anim?.clipToOutline = true
btn_anim_0.setOnClickListener {
val status = Status(300.0, -500.0, 0.0, 600.0)
AnimatorSet().apply {
play(getAnim(status)).before(getAnim(status.nextStatus, true))
}.start()
}
}
private fun getAnim(status: Status, end: Boolean = false) =
ValueAnimator.ofObject(
TypeEvaluator<Status> { fraction, startValue, _ ->
val t = startValue.duration * fraction // 当前经过时间
val curWidth = startValue.vx * t
val curHigh = 0.5 * Status.a * t * t + startValue.vy * t
Status(startValue.vx, startValue.vy + Status.a * t, curWidth, curHigh)
},
status,
status.nextStatus
).apply {
duration = (status.duration * 1000).toLong()
interpolator = LinearInterpolator()
addUpdateListener {
(it.animatedValue as Status).let { curStatus ->
view_anim.translationX = curStatus.width.toFloat()
view_anim.translationY = curStatus.high.toFloat()
}
}
if (!end) {
doOnEnd {
view_anim.top += status.y.toInt()
view_anim.bottom += status.y.toInt()
view_anim.left += status.x.toInt()
view_anim.right += status.x.toInt()
}
}
}
/**
* @param vx 横向初速度
* @param vy 纵向初速度
* @param width 横向位移
* @param high 纵向位移
*/
data class Status(val vx: Double, val vy: Double, val width: Double, val high: Double) {
val duration: Double by lazy { // 当前状态的落地时间
// high = 0.5*a*duration*duration + vy*duration
Util.getAns(0.5 * a, vy, -high) // 耗时
}
val nextStatus: Status by lazy {
Status(vx * decay, -(vy + a * duration) * decay, vx * duration, 0.0)
}
val x by lazy {
duration * vx
}
val y by lazy {
duration * vy + 0.5 * a * duration * duration
}
companion object {
// 衰减系数
const val decay = 0.8
// 加速度
const val a = 1000
}
}
}
效果如下:
横向速度为300,纵向速度为-500 横向速度为300,纵向速度为0
进阶
上诉动画仅仅使用估值器实现了小球跳跃两次的动画,自定义了各种状态看起来较复杂,其实使用属性动画中的ObjectAnimator也能完成上诉的功能,而且简单易懂。下篇文章教你。
网友评论