安卓-属性动画和硬件加速

属性动画和硬件加速

属性动画

ViewPropertyAnimator

使⽤View.animate()创建对象,以及使⽤ViewPropertyAnimator.translationX()等⽅法来设置动画;
可以连续调⽤来设置多个动画;
可以⽤setDuration()来设置持续时间;
可以⽤setStartDelay()来设置开始延时;
以及其他⼀些便捷⽅法。

ObjectAnimator

使⽤ObjectAnimator.ofXxx()来创建对象,以及使⽤ObjectAnimator.start()来主动启动动画。它的优势在于,可以为⾃定义属性设置动画。

1
ObjectAnimatoranimator=ObjectAnimator.ofObject(view, "radius", Utils.dp2px(200));

另外,⾃定义属性需要设置 gettersetter ⽅法,并且 setter ⽅法⾥需要调⽤invalidate()来触发重绘:

1
2
3
4
5
6
7
publicfloatgetRadius() {
returnradius;
}
publicvoidsetRadius(floatradius) {
this.radius=radius;
invalidate();
}

可以使⽤setDuration()来设置持续时间;
可以⽤setStartDelay()来设置开始延时;
以及其他⼀些便捷⽅法。

Interpolator

插值器,⽤于设置时间完成度到动画完成度的计算公式,直⽩地说即设置动画的速度曲线,通过setInterpolator(Interpolator)⽅法来设置。
常⽤的有AccelerateDecelerateInterpolator AccelerateInterpolator
DecelerateInterpolator LinearInterpolator

PropertyValuesHolder

⽤于设置更加详细的动画,例如多个属性应⽤于同⼀个对象:

1
2
3
PropertyValuesHolderholder1=PropertyValuesHolder.ofFloat("radius", Utils.dp2px(200));
PropertyValuesHolderholder2=PropertyValuesHolder.ofFloat("offset", Utils.dp2px(100));
ObjectAnimatoranimator=PropertyValuesHolder.ofPropertyValuesHolder(view, holder1, holder2);

或者,配合使⽤Keyframe,对⼀个属性分多个段:

1
2
3
4
5
Keyframekeyframe1=Keyframe.ofFloat(0, Utils.dpToPixel(100));
Keyframekeyframe2=Keyframe.ofFloat(0.5f, Utils.dpToPixel(250));
Keyframekeyframe3=Keyframe.ofFloat(1, Utils.dpToPixel(200));
PropertyValuesHolderholder=PropertyValuesHolder.ofKeyframe("radius", keyframe1, keyframe2, keyframe3);
ObjectAnimatoranimator=ObjectAnimator.ofPropertyValuesHolder(view, holder);

AnimatorSet

将多个 Animator 合并在⼀起使⽤,先后顺序或并列顺序都可以:

1
2
3
AnimatorSetanimatorSet=newAnimatorSet();
animatorSet.playTogether(animator1, animator2);
animatorSet.start();

TypeEvaluator

⽤于设置动画完成度到属性具体值的计算公式。默认的ofInt() ofFloat()已经有了⾃带的IntEvaluatorFloatEvaluator,但有的时候需要⾃⼰设置 Evaluator。例如,对于颜⾊,需要为 int 类型的颜⾊设置 ArgbEvaluator,⽽不是让它们使⽤ IntEvaluator

1
animator.setEvaluator(newArgbEvaluator());

如果你对 ArgbEvaluator 的效果不满意,也可以⾃⼰写⼀个HsvEvaluator

1
2
3
4
5
6
publicclassHsvEvaluatorimplementsTypeEvaluator<Integer> {
@Override
publicObjectevaluate(floatfraction, ObjectstartValue, Object endValue) {
...
}
}

另外,对于不⽀持的类型,也可以使⽤ofObject()来在创建 Animator 的同时就设置上
Evaluator,⽐如NameEvaluator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
publicclassNameEvaluatorimplementsTypeEvaluator<String> {
List<String>names= ...;
@Override
publicStringevaluate(floatfraction, StringstartValue, String endValue) {
if (!names.contains(startValue)) {
thrownewIllegalArgumentException("Start value not existed");
}
if (!names.contains(endValue)) {
thrownewIllegalArgumentException("End value not existed");
}
intindex= (int) ((names.indexOf(endValue) - names.indexOf(startValue)) *fraction);
returnnames.get(index);
}
}
ObjectAnimatoranimator=ObjectAnimator.ofObject(view, "name", new NameEvaluator(), "Jack");

Listeners

View 的点击、⻓按监听器⼀样,Animator 也可以使⽤setXxxListener() addXxxListener()来设置监听器。

ValueAnimator

这是最基本的 Animator,它不和具体的某个对象联动,⽽是直接对两个数值进⾏渐变计算。使⽤很少。

硬件加速

硬件加速是什么

  • 使⽤ CPU 绘制到 Bitmap,然后把 Bitmap 贴到屏幕,就是软件绘制;
  • 使⽤ CPU 把绘制内容转换成 GPU 操作,交给 GPU,由 GPU 负责真正的绘制,就叫硬件绘制;
  • 使⽤ GPU 绘制就叫做硬件加速

怎么就加速了?

  • GPU 分摊了⼯作
  • GPU 绘制简单图形(例如⽅形、圆形、直线)在硬件设计上具有先天优势,会更快
  • 流程得到优化(重绘流程涉及的内容更少)

硬件加速的缺陷:

兼容性。由于使⽤ GPU 的绘制(暂时)⽆法完成某些绘制,因此对于⼀些特定的 API,需要关闭硬件
加速来转回到使⽤ CPU 进⾏绘制。

离屏缓冲:

  • 离屏缓冲是什么:单独的⼀个绘制 View(或 View 的⼀部分)的区域
  • setLayerType()saveLayer()
    • setLayerType() 是对整个 View,不能针对 onDraw() ⾥⾯的某⼀具体过程
      • 这个⽅法常⽤来关闭硬件加速,但它的定位和定义都不只是⼀个「硬件加速开关」。
        它的作⽤是为绘制设置⼀个离屏缓冲,让后⾯的绘制都单独写在这个离屏缓冲内。如果参数填写LAYER_TYPE_SOFTWARE,会把离屏缓冲设置为⼀个 Bitmap ,即使⽤软件绘制来进⾏缓冲,这样就导致在设置离屏缓冲的同时,将硬件加速关闭了。但需要知道,这个⽅法被⽤来关闭硬件加速,只是因为 Android 并没有提供⼀个便捷的⽅法在 View 级别简单地开关硬件加速⽽已。
    • saveLayer() 是针对 Canvas 的,所以在 onDraw() ⾥可以使⽤ saveLayer() 来圈出具体哪部
      分绘制要⽤离屏缓冲
      • 然⽽……最新的⽂档表示这个⽅法太重了,能不⽤就别⽤,尽量⽤ setLayerType() 代替