oynix

于无声处听惊雷,于无色处见繁花

PropertyValuesHolder动画

PropertyValuesHolder,准确来讲并不是一种动画,而是种生成动画Animator的方式。

1. 引言

常用的生成一个单独的动画的方式,一般是这样,

1
2
3
4
// 在imageView上生成一个透明度变化的动画
val anim = ObjectAnimator.ofFloat(imageView, "alpha", 0f, 1f)
anim.duration = 1000L
anim.start()

生成两个动画,并执行,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
val alphaAnim = ObjectAnimator.ofFloat(imageView, "alpha", 0f, 1f, 0f)
alphaAnim.duration = 1000L

val rotationAnim = ObjectAnimator.ofFloat(imageView, "rotation", 0f, 360f, 720f)
rotationAnim.duration = 1000L

// 生成一个Set
val set = AnimatorSet()

// 顺序播放两个动画
set.playSequentially(alphaAnim, rotationAnim)
set.start()

// 同时播放两个动画
set.playTogether(alphaAnim, rotationAnim)
set.start()

2. 平替ObjectAnimator.ofFloat

使用PropertyValuesHolder也可以实现以上的动画,二者的区别在于,ObjectAnimator是在一个View上执行某个属性的动画,而PropertyValuesHolder则是不关心View,只关心属性值的变化,所以它没有View,正如名字所言,是一个属性值的Holder,

1
2
3
4
5
6
7
// 表示alpha属性从0变化到1
val holder = PropertyValuesHolder.ofFloat("alpha", 0f, 1f)

// 将属性值的变化应用到某个View上,以形成动画
val anim = ObjectAnimator.ofPropertyValuesHolder(imageView, holder)
anim.duration = 1000L
anim.start()

当有多个属性动画时,PropertyValuesHolder的方式最终只会生成一个Animator,而不是多个Animator,再通过Set组合,相比于ObjectAnimator方式省去了一些样板代码,

1
2
3
4
5
6
7
val alphaHolder = PropertyValuesHolder.ofFloat("alpha", 0f, 1f)
val rotationHolder = PropertyValuesHolder.ofFloat("rotation", 0f, 360f)

// 将属性值的变化应用到某个View上,以形成动画
val anim = ObjectAnimator.ofPropertyValuesHolder(imageView, alphaHolder, rotationHolder)
anim.duration = 1000L
anim.start()

3. Keyframe关键帧

上面两种方式虽然略微有所不同,但是有个共同点:动画时长都是均匀分配的,如,

1
2
val anim = ObjectAnimator(imageView, "alpha", 0f, 0.5f, 1f)
anim.duration = 1000f

透明度的变化值传入了3个值,将整个动画过程分成了2个区间,0-0.5和0.5-1,2个区间平分1000的时长,每个区间占500,PropertyValuesHolder方式也是如此,不论有多少个动画区间,每个区间都会平分整个动画时长。

但有时的需求并不希望平分,比如希望从0变化到0.5只占用三分之一的动画时长,从0.5到1占用剩下的时长,这个时候怎么办呢?当然,可以手动计算,然后修改动画区间的数量,比如这个情况,使用(0f, 0.5f, 0.75f, 1f),增加一个0.75f,将动画区间增加到3个,此时0到0.5刚好占一个,也就是三分之一。

手动计算,实乃下下策,这个时候,Keyframe,关键帧,就派上了用场,

1
2
3
val key1 = Keyframe.ofFloat(0f, 0f)
val key2 = Keyframe.ofFloat(0.33f, 0.5f)
val key3 = Keyframe.ofFloat(1f, 1f)

Keframe接收两个参数,第一个是帧的位置,通俗讲就是动画的进度,第二个是在这一帧时的值,上面这三个关键帧就很好理解了。之前是填入固定了属性值给Holder,现在是通过Keyframe来管理属性值。通过关键帧生成Holder,之后的操作不变,还是老样子,

1
2
3
4
5
val holder = PropertyValuesHolder.ofKeyframe("alpha", key1, key2, key3)

val anim = ObjectAnimator.ofPropertyValuesHolder(imageView, holder)
anim.duration = 1000L
anim.start()

4. 插补器interpolator

插补器是用来控制变化的快慢的,比如线性插补器控制动画属性值从开始到结束均匀变化,加速插补器会让属性值变化越来越快,设置给Animator的插补器是控制整个动画过程的,而Keyframe是可以针对每一帧的变化设置插补器,

1
2
3
4
5
6
val key1 = Keyframe.ofFloat(0f, 0f)

val key2 = Keyframe.ofFloat(0.33f, 0.5f)
key2.interplator = DecelerateInterpolator()

val key3 = Keyframe.ofFloat(1f, 1f)

我在key2上设置了一个减速插补器,表示从key2的上一帧到key2属性值会减速变化。这也就意味着,第一帧不能设置插补器,因为它没有上一帧。

5. 总结

  • 当生成单个动画时,两种方式区别不大。
  • 当需要生成多个动画组合时,PropertyValuesHolder的方式可以减少一些代码量。
  • 当需要控制属性值区间非均匀变化时,可使用Keyframe。
------------- (完) -------------
  • 本文作者: oynix
  • 本文链接: https://oynix.com/2022/08/0d756bb512d6/
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

欢迎关注我的其它发布渠道