自定义 View 属性动画 HenCoder-6 [MD]
| 我的GitHub | 我的博客 | 我的微信 | 我的邮箱 |
|---|---|---|---|
| baiqiantao | baiqiantao | bqt20094 | baiqiantao@sina.com |
目录
扔物线自定义 View 系列教程总结-6
全文整理自 扔物线(HenCoder)自定义 View 系列文章
重新整理的目标:
- 内容压缩:去除活跃气氛的段子、图片,去除无意义的解释、代码,去除不刚兴趣的内容,压缩比至少 50%
- 排版优化:更清晰的结构,更精简的标题,更规范的缩进、标点符号、代码格式,好的结构才能更好的吸收
- MarkDown:以标准的 MarkDown 格式重新编排,纯文本更易迭代维护
扔物线自定义 View 系列教程分
绘制、布局和触摸反馈三部分内容。
属性动画
ViewPropertyAnimator
使用方式:View.animate() 后跟 translationX() 等方法,动画会自动执行。

view.animate().translationX(500).setDuration(500);
带有
By后缀的是增量版本的方法,例如,translationX(100)表示用动画把View的translationX值渐变为100,而translationXBy(100)则表示用动画把View的translationX值渐变地增加100。
ObjectAnimator
使用方式:
- 如果是自定义控件,需要添加
setter/getter方法 - 用
ObjectAnimator.ofXXX()创建ObjectAnimator对象 - 用
start()方法执行动画
public class SportsView extends View {
float progress = 0;
// 创建 getter 方法
public float getProgress() {
return progress;
}
// 创建 setter 方法
public void setProgress(float progress) {
this.progress = progress;
invalidate();
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawArc(arcRectF, 135, progress * 2.7f, false, paint);
}
}
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "progress", 0, 65);
animator.setDuration(2000);
animator.start();
Interpolator 速度模型
常用的 Interpolator
- AccelerateDecelerateInterpolator 先加速再减速
- 这是默认的
Interpolator,也是最符合现实中物体运动的Interpolator,所以如果你要做的是最简单的状态变化,那么一般就用这个默认的最好。 - 它的动画效果 看起来就像是物体从速度为 0 开始逐渐加速,然后再逐渐减速直到 0 的运动。它的
速度 / 时间曲线以及动画完成度 / 时间曲线都是一条正弦/余弦曲线。
- 这是默认的
- LinearInterpolator 匀速
- AccelerateInterpolator 持续加速
- 在整个动画过程中,一直在加速,直到动画结束的一瞬间,直接停止。
- 它主要用在离场效果中,比如某个物体从界面中飞离,就可以用这种效果。
- 它给人的感觉就会是「这货从零起步,加速飞走了」,到了最后动画骤停的时候,物体已经飞出用户视野,看不到了,所以他们是并不会察觉到这个骤停的。
- DecelerateInterpolator 持续减速直到 0
- 动画开始的时候是最高速度,然后在动画过程中逐渐减速,直到动画结束的时候恰好减速到 0。
- 它主要用于入场效果,比如某个物体从界面的外部飞入界面后停在某处。
- 它给人的感觉会是「咦飞进来个东西,让我仔细看看,哦原来是 XXX」。
- AnticipateInterpolator 先回拉一下再进行正常动画轨迹
- 效果看起来有点像投掷物体或跳跃等动作前的蓄力。
- 如果是平移动画,那么就是位置上的回拉;如果是放大动画,那么就是先缩小一下再放大。
- OvershootInterpolator 动画会超过目标值一些,然后再弹回来
- 效果看起来有点像你一屁股坐在沙发上后又被弹起来一点的感觉。
- AnticipateOvershootInterpolator 上面这两个的结合版:开始前回拉,最后超过一些然后回弹。
- BounceInterpolator 在目标值处弹跳,有点像玻璃球掉在地板上的效果。
- CycleInterpolator 这个也是一个
正弦/余弦曲线- 和
AccelerateDecelerateInterpolator的区别是,它可以自定义曲线的周期 - 所以动画可以不到终点就结束,也可以到达终点后回弹
- 回弹的次数由曲线的周期决定,曲线的周期由
CycleInterpolator()构造方法的参数决定
- 和
- PathInterpolator 自定义
动画完成度/时间完成度曲线- 用这个可以定制出任何你想要的速度模型
- 定制的方式是使用一个
Path对象来绘制出你要的动画完成度/时间完成度曲线
PathInterpolator 案例
Path path = new Path();
path.lineTo(1, 1); // 匀速

Path path = new Path();
path.lineTo(0.25f, 0.25f); // 先以「动画完成度 : 时间完成度 = 1 : 1」的速度匀速运行 25%
path.moveTo(0.25f, 1.5f); // 然后瞬间跳跃到 150% 的动画完成度
path.lineTo(1, 1); // 再匀速倒车,返回到目标点

这条 Path 描述的其实是一个 y = f(x) (0 < x; x < 1) (y 为动画完成度,x 为时间完成度)的曲线,所以同一段时间完成度上不能有两段不同的动画完成度,而且每一个时间完成度的点上都必须要有对应的动画完成度,否则会导致程序 FC。
新增加的 Interpolator
除了上面的这些,Android 5.0 (API 21)引入了三个新的 Interpolator 模型,并把它们加入了 support v4 包中。这三个新的 Interpolator 每个都和之前的某个已有的 Interpolator 规则相似,只有略微的区别。
FastOutLinearInInterpolator
它和 AccelerateInterpolator 一样,都是一个持续加速的运动路线。只不过 FastOutLinearInInterpolator 的曲线公式是用的贝塞尔曲线,而 AccelerateInterpolator 用的是指数曲线。具体来说,它俩最主要的区别是 FastOutLinearInInterpolator 的初始阶段加速度比 AccelerateInterpolator 要快一些。

FastOutLinearInInterpolator红色,AccelerateInterpolator绿色
实际上,这点区别,在实际应用中用户根本察觉不出来。而且,AccelerateInterpolator 还可以在构造方法中调节变速系数,分分钟调节到和 FastOutLinearInInterpolator(几乎)一模一样。所以你在使用加速模型的时候,这两个选哪个都一样,没区别的。
FastOutSlowInInterpolator
先加速再减速。FastOutSlowInInterpolator 用的是贝塞尔曲线, AccelerateDecelerateInterpolator 用的是正弦/余弦曲线。具体来讲, FastOutSlowInInterpolator 的前期加速度要 快得多。

FastOutSlowInInterpolator红色,AccelerateDecelerateInterpolator绿色
FastOutSlowInInterpolator 的前期加速更猛一些,后期的减速过程的也减得更迅速。用更直观一点的表达就是, AccelerateDecelerateInterpolator 像是物体的自我移动,而 FastOutSlowInInterpolator 则看起来像有一股强大的外力「推」着它加速,在接近目标值之后又「拽」着它减速。总之, FastOutSlowInterpolator 看起来有一点「着急」的感觉。
LinearOutSlowInInterpolator
持续减速。它和 DecelerateInterpolator 主要区别在于,LinearOutSlowInInterpolator 的初始速度更高。

LinearOutSlowInInterpolator红色,DecelerateInterpolator绿色
动画监听
ViewPropertyAnimator 可以用 setListener() 和 setUpdateListener() 方法设置一个监听器,通过 set[Update]Listener(null) 来移除。
ObjectAnimator 可以用 addListener() 和 addUpdateListener() 来添加一个或多个监听器,通过 remove[Update]Listener() 来指定移除对象。
由于 ObjectAnimator 支持使用 pause() 方法暂停,所以它还多了一个 addPauseListener() / removePauseListener() 的支持
而 ViewPropertyAnimator 则独有 withStartAction() 和 withEndAction() 方法,可以设置一次性的动画开始或结束的监听。
AnimatorListener
ViewPropertyAnimator.setListener() / ObjectAnimator.addListener()
回调方法:
onAnimationStart(Animator animation)当动画开始执行时被调用onAnimationEnd(Animator animation)当动画结束时被调用onAnimationCancel(Animator animation)当动画被通过cancel()方法取消时被调用onAnimationRepeat(Animator animation)动画重复执行时被调用- 可通过
setRepeatMode()/setRepeatCount()或repeat()方法让动画重复执行 - 由于
ViewPropertyAnimator不支持重复,所以这个方法对ViewPropertyAnimator无效
- 可通过
需要说明一下的是,就算动画被取消, onAnimationEnd() 也会被调用。所以当动画被取消时,如果设置了 AnimatorListener,那么 onAnimationCancel() 和 onAnimationEnd() 都会被调用。 onAnimationCancel() 会先于 onAnimationEnd() 被调用。
AnimatorUpdateListener
ViewPropertyAnimator.setUpdateListener() / ObjectAnimator.addUpdateListener()
它只有一个回调方法:onAnimationUpdate(ValueAnimator animation) 当动画的属性更新时被调用。
参数 ValueAnimator 是 ObjectAnimator 的父类,也是 ViewPropertyAnimator 的内部实现,所以这个参数其实就是 ViewPropertyAnimator 内部的那个 ValueAnimator,或者对于 ObjectAnimator 来说就是它自己本身。
ValueAnimator 有很多方法可以用,它可以查看当前的动画完成度、当前的属性值等等,具体内容后面再讲。
withStartAction/EndAction()
这两个方法是 ViewPropertyAnimator 的独有方法。它们和 set/addListener() 中回调的 onAnimationStart() / onAnimationEnd() 相比起来的不同主要有两点:
withStartAction()/withEndAction()是一次性的,在动画执行结束后就自动弃掉了,就算之后再重用ViewPropertyAnimator来做别的动画,用它们设置的回调也不会再被调用。而set/addListener()所设置的AnimatorListener是持续有效的,当动画重复执行时,回调总会被调用。withEndAction()设置的回调只有在动画正常结束时才会被调用,而在动画被取消时不会被执行。这点和AnimatorListener.onAnimationEnd()的行为是不一致的。
TypeEvaluator 类型解析
在实际的开发中,除了可以用 ofInt() 来做整数的属性动画和用 ofFloat() 来做小数的属性动画外,可以做属性动画的还可以是其他类型。当需要对其他类型来做属性动画的时候,就需要用到 TypeEvaluator 了。
TypeEvaluator 可以让你对同样的属性有不同的解析方式,有了 TypeEvaluator,你的属性动画就有了更大的灵活性,从而有了无限的可能。
ArgbEvaluator
TypeEvaluator 最经典的用法是使用 ArgbEvaluator 来做颜色渐变的动画。
ObjectAnimator animator = ObjectAnimator.ofInt(view, "color", 0xffff0000, 0xff00ff00);
animator.setEvaluator(new ArgbEvaluator());
animator.start();
//在 Android 5.0(API 21) 以上可以直接使用下面的方式
ObjectAnimator.ofArgb(view, "color", 0xffff0000, 0xff00ff00).start();

自定义 TypeEvaluator
自定义 HslEvaluator,把 ARGB 转换成 HSV
private class HsvEvaluator implements TypeEvaluator {
float[] startHsv = new float[3];
float[] endHsv = new float[3];
float[] outHsv = new float[3];
@Override
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
// 把 ARGB 转换成 HSV
Color.colorToHSV(startValue, startHsv);
Color.colorToHSV(endValue, endHsv);
// 计算当前动画完成度(fraction)所对应的颜色值
if (endHsv[0] - startHsv[0] > 180) {
endHsv[0] -= 360;
} else if (endHsv[0] - startHsv[0] < -180) {
endHsv[0] += 360;
}
outHsv[0] = startHsv[0] + (endHsv[0] - startHsv[0]) * fraction;
if (outHsv[0] > 360) {
outHsv[0] -= 360;
} else if (outHsv[0] < 0) {
outHsv[0] += 360;
}
outHsv[1] = startHsv[1] + (endHsv[1] - startHsv[1]) * fraction;
outHsv[2] = startHsv[2] + (endHsv[2] - startHsv[2]) * fraction;
// 计算当前动画完成度(fraction)所对应的透明度
int alpha = startValue >> 24 + (int) ((endValue >> 24 - startValue >> 24) * fraction);
// 把 HSV 转换回 ARGB 返回
return Color.HSVToColor(alpha, outHsv);
}
}
使用自定义的 HslEvaluator
ObjectAnimator animator = ObjectAnimator.ofInt(view, "color", 0xff00ff00);
animator.setEvaluator(new HsvEvaluator()); // 使用自定义的 HslEvaluator
animator.start();

ofObject()
借助于 TypeEvaluator,属性动画就可以通过 ofObject() 来对不限定类型的属性做动画了。
//在 API 21 中,已经自带了 PointFEvaluator 这个类
private class PointFEvaluator implements TypeEvaluator {
PointF newPoint = new PointF();
@Override
public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
float x = startValue.x + (fraction * (endValue.x - startValue.x));
float y = startValue.y + (fraction * (endValue.y - startValue.y));
newPoint.set(x, y);
return newPoint;
}
}
ObjectAnimator animator = ObjectAnimator.ofObject(view, "position",
new PointFEvaluator(), new PointF(0, 0), new PointF(1, 1));
animator.start();

复杂的属性关系动画
- 使用
PropertyValuesHolder来对多个属性同时做动画 - 使用
AnimatorSet来同时管理调配多个动画 - 使用
PropertyValuesHolder.ofKeyframe()来把一个属性拆分成多段,执行更加精细的属性动画
PropertyValuesHolder 多属性
很多时候,你在同一个动画中会需要改变多个属性,例如在改变透明度的同时改变尺寸。如果使用 ViewPropertyAnimator,你可以直接用连写的方式来在一个动画中同时改变多个属性:
view.animate().scaleX(1).scaleY(1).alpha(1);
而对于 ObjectAnimator,是不能这么用的。不过你可以使用 PropertyValuesHolder 来同时在一个动画中改变多个属性。
PropertyValuesHolder holder1 = PropertyValuesHolder.ofFloat("scaleX", 1);
PropertyValuesHolder holder2 = PropertyValuesHolder.ofFloat("scaleY", 1);
PropertyValuesHolder holder3 = PropertyValuesHolder.ofFloat("alpha", 1);
ObjectAnimator.ofPropertyValuesHolder(view, holder1, holder2, holder3).start();
AnimatorSet 多动画
有的时候,你不止需要在一个动画中改变多个属性,还会需要多个动画配合工作,比如,在内容的大小从 0 放大到 100% 大小 后 开始移动。这种情况使用 PropertyValuesHolder 是不行的,因为这些属性如果放在同一个动画中,需要共享动画的开始时间、结束时间、Interpolator 等等一系列的设定,这样就不能有 先后次序 地执行动画了。这就需要用到 AnimatorSet 了。
有了 AnimatorSet ,你就可以对多个 Animator 进行统一规划和管理,让它们按照要求的顺序来工作。
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playSequentially(animator1, animator2); // 两个动画依次执行
//animatorSet.playTogether(animator1, animator2); // 两个动画同时执行
animatorSet.start();
还可以使用 AnimatorSet.play(animatorA).with/before/after(animatorB) 的方式来精确配置各个 Animator 之间的关系
animatorSet.play(animator1).with(animator2);
animatorSet.play(animator1).before(animator2);
animatorSet.play(animator1).after(animator2);
animatorSet.start();
Keyframe 关键帧
除了合并多个属性和调配多个动画,你还可以在 PropertyValuesHolder 的基础上更进一步,通过设置 Keyframe (关键帧),把同一个动画属性拆分成多个阶段。例如,你可以让一个进度增加到 100% 后再「反弹」回来。
Keyframe keyframe1 = Keyframe.ofFloat(0, 0); // 在 0% 处开始
Keyframe keyframe2 = Keyframe.ofFloat(0.5f, 100); // 时间 50% 的时候,动画完成度 100%
Keyframe keyframe3 = Keyframe.ofFloat(1, 80); // 时间 100% 的时候,动画完成度 80%,即反弹 20%
PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("progress", keyframe1, keyframe2, keyframe3);
ObjectAnimator.ofPropertyValuesHolder(view, holder).start();

ValueAnimator 最基本的轮子
除了 ViewPropertyAnimator 和 ObjectAnimator,还有第三个选择是 ValueAnimator。很多时候,你用不到它,因为它的功能太基础了,只是在你使用一些第三方库的控件,而你想要做动画的属性却没有 setter/getter 方法的时候,会需要用到它。
- ValueAnimator 是 ObjectAnimator 的父类,实际上,ValueAnimator 就是一个不能指定
目标对象的 ObjectAnimator - ObjectAnimator 是自动调用目标对象的
setter方法来更新目标属性的值,以及很多的时候还会以此来改变目标对象的 UI - ValueAnimator 只是通过渐变的方式来改变一个独立的
数据,这个数据不是属于某个对象的,至于在数据更新后要做什么事,全都由你来定。 - ViewPropertyAnimator、ObjectAnimator、ValueAnimator 这三种 Animator从左到右依次变得更加难用了,但也更加灵活了。但是它们的性能是一样的,内部实现其实都是 ValueAnimator。
ValueAnimator 功能最少、最不方便,但有时也是束缚最少、最灵活。比如有的时候,你要给一个第三方控件做动画,你需要更新的那个属性没有
setter方法,只能直接修改,这样的话 ObjectAnimator 就不灵了啊。怎么办?这个时候你就可以用 ValueAnimator,在它的onUpdate()里面更新这个属性的值,并且手动调用invalidate()。
在实际使用时候,只需遵循一个原则:尽量用简单的!能用 View.animate() 实现就不用 ObjectAnimator,能用 ObjectAnimator 就不用 ValueAnimator。
2021-5-5
本文来自博客园,作者:白乾涛,转载请注明原文链接:https://www.cnblogs.com/baiqiantao/p/14733139.html

浙公网安备 33010602011771号