属性动画
属性动画(Property Animation) 是 API Level 11
时被引入的,Android 3.0
才开始有 属性动画相关的 API.
先来看看网上的一个开源控件,动画还是很酷炫,基本包含了属性动画该有的大部分知识内容.
1. ValueAnimator
ValueAnimator是属性动画的核心类,下面会讲到 TimeAnimator
, ObjectAnimator
就是它的子类。
调用的几个方式:
ofObject是直接把一个对象过渡到另外一个对象,其它的 ofArgb/ofFloat/ofInt 是将值过渡到另一个值
ValueAnimator.ofObject(TypeEvaluator evaluator/*估值器,下个章节有介绍*/, Object... values)
ValueAnimator.ofArgb(int... values)
ValueAnimator.ofFloat(float.. values)
ValueAnimator.ofInt(int... values)
ValueAnimator.ofPropertyValuesHolder(PropertyValuesHolder... values)
举个栗子:
// ValueAnimator.ofObject demo
ValueAnimator objectAnimator = ValueAnimator.ofObject(
new PointEvaluator(), new Point(10, 10), new Point(100, 100));
objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mPoint = (Point) animation.getAnimatedValue();
invalidate();
}
});
objectAnim.setDuration(2000);
objectAnim.setRepeatCount(ValueAnimator.INFINITE);
objectAnim.setRepeatMode(ValueAnimator.RESTART);
objectAnim.start(); // 启动动画
# 刷新UI绘制界面
@Override
protected void onDraw(Canvas canvas) {
canvas.drawCircle(mPoint.x, mPoint.y, 50, mPaint);
}
涉及的知识点:
函数名 | 说明 |
---|---|
Animator.start() |
启动动画 |
Animator.end() |
结束动画 |
Animator.cancel() |
取消动画 |
Animator.pause() |
暂停动画 API 19新增的方法 |
Animator.resume() |
重新启动 API 19新增的方法 |
Animator.setDuration(int) |
设置动画时间 |
setRepeatCount(int) |
动画重复次数(大于等于 0),值为 小于0 或 ValueAnimator.INFINITE 时,无限循环 |
setRepeatMode |
设置循环模式,ValueAnimator.RESTART: 重新从头开始执行 ;ValueAnimator.REVERSE:反方向执行 |
addUpdateListener |
监听数值变化, ValueAnimator.AnimatorUpdateListener -> onAnimationUpdate 通过数值返回进行一序列相关操作 |
动画监听器 addListener |
Animator.AnimatorListener 相关函数说明: onAnimationStart() - 在动画开始播放时调用;onAnimationEnd() - 在动画结束播放时调用/取消的动画也会调用; onAnimationRepeat() - 在动画重复播放时调用;onAnimationCancel() - 在动画取消播放时调用; |
像几年前在公司写的输入法的移动边框的动画,就是用的 ValueAnimator + onDraw绘制(onAnimationUpdate更新相关值) 出来的动画效果
其实有一些酷炫的动画(当然这里你需要了解android绘图的相关知识(比如绘图,混合模式,Canvas,Paint等等),也是用这种方式弄出来的.
还有一个很酷炫的爆炸粒子效果
相关资料学习地址
1.1 ObjectAnimator
ValueAnimator 有一个缺点,如果对某个控件执行动画,就需要监听 ValueAnimator 的动画过程,相比补间动画(View动画)要繁琐的多。
为了调用简单方便的去 动画对应的控件,所以就产生了 ObjectAnimator(继承自ValueAnimator,所以ValueAnimator的函数ObjectAnimator都可以调用
) ;
ObjectAnimator 重写了几个函数,ofInt()
, ofFloat(object target, String propertyName, float... values)
,ofArgb()
等等;来看看函数的使用方式的小栗子;
实现了 放大 X 的动画
// 第一个参数传入需要做动画的对象(target) 第二个参数 是属性,第三个参数是 可变参数,是值.
// 现在就是 放大 X,从 1.0f ~ 2.4f
// 之所以可以动画,是因为 view 里面有 setScaleX(float scaleX) 这个函数,ObjectAnimator对这个 view 的 scaleX 自动赋值.
ObjectAnimator animator = ObjectAnimator.ofFloat(view, View.SCALE_X/*scaleX*/, 1.0f, 2.4f);
animator.setDuration(300);
animator.start();
ObjectAnimator动画流程了解
ofFloat(view, “scaleX”, 1.0f, 2.4f) -> 插值器 -> 估值器 -> 调用set函数(主要是反射+拼装)
可以改变的控件属性列表:scaleX
,scaleY
,translationX
,translationY
,alpha
,rotation
等等。(主要是类里面可以设置的属性值,都可以改变,需要有 setXXX
才行)
旋转
我们还可以自定义ObjectAnimator
- 为对象设置需要操作属性的
set()
与get()
方法 - 通过实现
TypeEvaluator(估值器)
类从而定义属性变化的逻辑(下下个章节有介绍)
来个小栗子(我们修改刚才的ValueAnimatror小球的移动的代码)
// 1. 给小球的View的类 添加了一个带 SetXXX 方式的函数
public void setPointX(int x) {
mPoint.x = x;
invalidate();
}
// 2. 因为是 int 类型,所以使用 ofInt
ObjectAnimator testAnim = ObjectAnimator.ofInt(view, "PointX", 100, 500);
testAnim.setDuration(888);
testAnim.start();
注意
如果想要改变控件的宽(Width),高(Height),是不行,因为setWidth不是改变的控件的宽度,所以需要这样改.
private static class ViewWrapper {
private View mTarget;
// 构造方法:传入需要包装的对象
public ViewWrapper(View target) {
mTarget = target;
}
public void setWidth(int width) {
mTarget.getLayoutParams().width = width;
... ..
}
public void setHeight(int height) ...
... ...
}
// 调用方式
ObjectAnimator widthAnim = ObjectAnimator.ofInt(new ViewWrapper(view), "width", 100, 500);
1.2 TimeAnimator 与 其它
TimeAnimator(继承自 ValueAnimator
) 不常用,此处不做篇幅介绍,感兴趣的小伙伴,请自行查阅官方文档或者搜索相关资料.
PropertyValuesHolder
官方文档资料
指定关键帧 Keyframe.ofFloat(float fraction, float value)
,(类似动画的关键帧,包含两个元素,时间点和位置)
// 小DEMO
// 生成了三个KeyFrame对象
// fraction:表示当前的显示进度,即从加速器中getInterpolation()函数的返回值;
// value:表示当前应该在的位置
KeyFrame frame0 = Keyframe.ofFloat(0f, 10f);
KeyFrame frame1 = Keyframe.ofFloat(0.1f, 120f);
KeyFrame frame3 = Keyframe.ofFloat(1.0f, 0f);
//
PropertyValuesHolder rotationHolder = PropertyValuesHolder.ofFloat("Rotation", frame0, frame1, frame2);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, rotationHolder);
animator.setDuration(3000);
animator.start();
Android 3.1 补充了 ViewPropertyAnimator 官方文档资料
// 谷歌官方为了便捷人性化的使用动画,Android 3.1 支持ViewPropertyAnimator 的方式.
view.animate().scaleX(1.2f).scaleY(1.2f).setDuration(300).start();
// 还包含(alhpa,translationX/Y,rotationX/Y....),具体参考文档,这里不进行详细的概述~!!
AnimatorSet
前面讲解的 ValueAnimator 与 ObjectAnimator,TimeAnimator 只能单独实现一个动画,如果我们想弄出复杂的动画效果,就需要用到 AnimatorSet
将他们串联起来。
如何去串联,这里涉及到两个重要的函数:
playSequentially(Animator... items | List<Animator> items):
顺序执行动画效果,这种情况类似,一分钟内 事情只能一个个来做playTogether(Animator... items | Collection<Animator> items):
同时并行执行动画效果,这种情况类似,在1分钟内 同时可以做几个事情
其它相关函数了解
类名 | 说明 |
---|---|
setDuration(long duration) | 设置动画时长 |
setInterpolator | 设置插值器 |
setTarget(Object target) | 设置目标对象,需要做动画的对象 |
举个栗子(模仿开始的酷炫菜单 搞一个小DEMO(菜单展开).
讲解下这个小demo的思路,
- 每个childView 添加 翻转 + 透明 到 集合Set.(注意,这里是同时并行执行动画效果,使用的playTogether)
- 将所有 childView 的 集合set,添加到 总的集合allSet(这里是顺序执行动画效果,使用的 playSequentially).
- 最终的效果就是,childView 一个个顺序 的 同时在 翻转+透明
// 初始化位置
for (int i = 1; i < mRootLayout.getChildCount(); i++) {
View view = mRootLayout.getChildAt(i);
view.setPivotY(0);
view.setRotationX(-90);
}
// 动画集合
AnimatorSet allSet = new AnimatorSet();
List<Animator> animList = new ArrayList<>();
for (int i = 1; i < mRootLayout.getChildCount(); i++) {
View childView = mRootLayout.getChildAt(i);
AnimatorSet set = new AnimatorSet();
// 同时并行执行 翻转 + 透明 的动画效果,加入集合 set
set.playTogether(rotationOpenVertical(childView), rotationOpenAlpha(childView));
animList.add(set);
}
// 顺序执行 childView 的动画效果,加入集合 allSet
allSet.setInterpolator(new AccelerateInterpolator());
allSet.playSequentially(animList);
allSet.setDuration(388);
allSet.start();
// 旋转
public static ObjectAnimator rotationOpenVertical(View v) {
return ObjectAnimator.ofFloat(v, "rotationX", -90, 0);
}
// 透明
public static ObjectAnimator rotationOpenAlpha(View v) {
return ObjectAnimator.ofFloat(v, View.ALPHA, 0, 1);
}
插值器(Interpolator)
设置插值器 - setInterpolator
插值器(加速器)说白了,就是 控制变化速率(指定动画如何变化变量) 的,比如 1秒(1000ms) 内 的 0 ~ 400 的距离,我们可以让这 0 ~ 400 区间 不是 匀速的,有快有慢,这就是插值器的作用;
除了下列的官方自带的插值器(参考链接1,参考链接2),插值器也可以进行自定义;
类名 | 说明 |
---|---|
AccelerateDecelerateInterpolator | 加速减速插值器,该插值器的变化速率在 开始和结束时缓慢, 中间加速。 |
AccelerateInterpolator | 加速插值器,动画开始的地方速率改变比较慢,然后开始加速 |
DecelerateInterpolator | 减速插值器,动画开始的一瞬间加速到最大值,然后逐渐变慢 |
LinearInterpolator | 线性插值器,匀速加速器 |
BounceInterpolator | 弹跳插值器,模拟自由落地后回弹的效果 |
AnticipateInterpolator | 初始偏移插值器,开始时 反方向移动一段距离 ,然后继续动画 |
OvershootInterpolator | 结束偏移插值器,结束时,超出结束位置,然后再回到结束位置 |
CycleInterpolator | 循环插值器,该插值器的动画会在指定数量的周期内重复。 |
AnticipateOvershootInterpolator | AnticipateInterpolator + OvershootInterpolator 的组合; |
TimeInterpolator | 该接口自定义自己的插值器。 |
举个栗子
不设置插值器,默认是 LinearInterpolator(匀速),看下效果
以控件顺序设置,进行插值器的设置,同样的时间3秒,同样的移动距离(translationX) 0~500
new AccelerateDecelerateInterpolator(); //(红色) 加速减速插值器,该插值器的变化速率在 开始和结束时缓慢, 中间加速
new DecelerateInterpolator(); //(黄色) 减速插值器,动画开始的一瞬间加速到最大值,然后逐渐变慢
new AccelerateInterpolator(); //(绿色) 加速插值器,动画开始的地方速率改变比较慢,然后开始加速
new LinearInterpolator(); //(蓝色) 线性插值器,匀速加速器
new AnticipateInterpolator(); //(x色) 初始偏移插值器,开始时 反方向移动一段距离 ,然后继续动画
从例子可以看出,插值器,主要是给动画增加了变化的速率
,让动画更有节奏,更好看一些,就像我们跑步一样,有时快,有时慢;
AnticipateInterpolator 的解析
// 原来是套入了一个数学公式 input * input * ((2.0f + 1) * input - 2.0f) 做了变化的速率
// 这样的话,如果需要弄一些其它的效果,就可以进行自定义插值器
public float getInterpolation(float input) {
float tension = 2.0f;
float value = input * input * ((tension + 1) * input - tension);
return value;
}
自定义插值器小栗子
// 继承 TimeInterpolator,重写 getInterpolation 函数,input 取值(0.0~1.0)
public class TestInterpolator implements TimeInterpolator {
@Override
public float getInterpolation(float input) {
return (float) (Math.log10(1 + 9 * input));
}
}
估值器(Evaluator)
插值器我们已经了解,是设置 变化速率的,那么估值器(Evaluator)是干什么的呢?看个正常效果的移动效果!!
再看看 增加了贝塞尔曲线公式的TypeEvaluator,看下效果,有抛物线的效果
从效果图,我们可以看出,估值器是改变了 具体数值;
用于控制动画如何从开始过渡到结束的,如:A(0,0) B(0,5)过两点之间的线可以是一条直线运动,也能是一条曲线,这个便由TypeEvaluator控制。
设置估值器 setEvaluator
类名 | 说明 |
---|---|
IntEvaluator | 这是用于计算 int 属性的值的默认评估程序。 |
FloatEvaluator | 这是用于计算 float 属性的值的默认评估程序。 |
ArgbEvaluator | 这是用于计算颜色属性的值(用十六进制值表示)的默认评估程序。 |
TypeEvaluator | 此接口用于创建您自己的估值器。添加动画效果的对象属性不是 int、float 或颜色,那么就必须实现 TypeEvaluator 接口,才能指定如何计算对象属性添加动画效果之后的值。如果想以不同于默认行为的方式处理 int、float和颜色,还可以自定义 TypeEvaluator。 |
看看其中ArgbEvaluator的小栗子
ValueAnimator colorValueAnimator = ValueAnimator.ofObject(
new ArgbEvaluator(), 0xFF00FF00, 0xFFFF0000, 0xFF0000FF);
colorValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
colorView.setBackgroundColor((Integer)animation.getAnimatedValue());
}
});
colorValueAnimator.setDuration(3000);
colorValueAnimator.start();
自定义TypeEvaluator 中 evaluate函数的几个参数
- fraction 参数是插值器中的返回值,表示当前动画的数值进度,百分制的小数点(0.0~1.0)
- startValue 开始值
- endValue 结束值
前面效果的 自定义 的 贝塞尔曲线公式的TypeEvaluator
自定义估值器需要实现 TypeEvaluator的接口 与 复写evaluate 函数
public class BezierEvaluator implements TypeEvaluator<Point> {
Point controlPoint;
public BezierEvaluator(Point point) {
controlPoint = point;
}
@Override
public Point evaluate(float fraction, Point startValue, Point endValue) {
float x = (1 - fraction) * (1 - fraction) * startValue.x + 2 * fraction * (1 - fraction) * controlPoint.x + fraction * fraction * endValue.x;
float y = (1 - fraction) * (1 - fraction) * startValue.y + 2 * fraction * (1 - fraction) * controlPoint.y + fraction * fraction * endValue.y;
// 返回对象动画过渡的逻辑计算后的值
return new Point((int)x, (int)y);
}
}
抛物线的比较常见的效果可以参考 饿了么等应用的效果
小总结
估值器最要是协助插值器实现 非线性动画;插值器和估值器都是一个接口,且内部都只有一个方法,我们只要实现接口就可以了,就可以做出很多绚丽的动画。
一般来说,插值器使用系统的就足够了,估值器自定义的可能会多一些,另外就是如果要对其他类型(非Int丶float丶color)做动画,必须自定义类型估值算法。
感兴趣的小伙伴的具体的 查询 官方资料 以及 网上的相关资料
在 XML 中声明属性动画
XML与Animator对应的3个标签
<animatro />:
对应 ValueAnimator
<animator
android:duration="int" # 动画执行时间
android:startOffset="int" # 对应代码 startDelay,延迟多久开始动画
android:valueFrom="float|int|color" # 起始值
android:valueTo="float|int|color" # 结束值
android:valueType="colorType|floatType|intType|pathType"
android:repeatCount="int" # 对应代码 setRepeatCount
android:repeatMode="restart(重新从头开始执行)|reverse(反方向执行)" # 对应代码 setRepeatMode
android:interpolator="@android:interpolator/xxx" # 对应代码 setInterpolator,设置插值器
/>
<objectAnimator />:
对应 ObjectAnimator
<objectAnimator
android:propertyName="string" # 属性名,比如 TranslationX,RotationX,Alpha 等
# 以下的属性与 animator 一致
android:duration="int"
android:startOffset="int"
android:valueFrom="float|int|color"
android:valueTo="float|int|color"
android:valueType="colorType|floatType|intType|pathType"
android:repeatCount="int|infinite"
android:repeatMode="restart|reverse"
android:interpolator="@android:interpolator/xxx"
/>
<propertyValuesHolder />:
对应 PropertyValuesHolder
<propertyValuesHolder
android:propertyName="string"
android:valueFrom="float|int|color"
android:valueTo="float|int|color"
android:valueType="colorType|floatType|intType|pathType"
/>
<set />:
对应 AnimatorSet
<set
android:ordering="together(同时并行执行)|sequentially(顺序依次执行)"
/>
注意需要将XML的动画文件放在 animator 目录下
举个栗子
# 旋转 + 移动 + 透明 的动画效果
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="together">
# 旋转 -720 ~ 0 度 X轴
<objectAnimator
android:duration="3500"
android:interpolator="@android:anim/bounce_interpolator"
android:propertyName="RotationX"
android:valueFrom="-720"
android:valueTo="0"
android:valueType="floatType" />
# 移动 0 ~ 500 X
<objectAnimator
android:duration="3500"
android:interpolator="@android:anim/bounce_interpolator"
android:propertyName="TranslationX"
android:valueFrom="0"
android:valueTo="500"
android:valueType="floatType" />
# 透明 0.1~1.0
<objectAnimator
android:duration="3500"
android:interpolator="@android:anim/bounce_interpolator"
android:propertyName="Alpha"
android:valueFrom="0.1"
android:valueTo="1.0"
android:valueType="floatType" />
</set>
看最终效果
场景小剧场
桌面的 图标 放大效果
唱盘机的旋转
歌词滚动
菜单
音乐控制器升级的手势动画效果
存在的问题
1. 动画再执行,Activity或者Fragment,其它的界面退出了,只能在每个生命周期结束的时候进行清除,很繁琐!!
2. AnimatorSet无法实现无限循环动画.(没有 setRepeatCount 函数). 需要给每个 Animator 动画去设置 setRepeatCount,很麻烦
3. 无法统一去设置插值器,只能统一写一个地方,然后调用
4. 弄一些复杂的动画,代码写了N多!!!
其它解决方案:
使用 我们团队之前写的 OpenAnim,能很方便的完成以上这些事情,我们团队现在已经投入使用,并且还支持生命周期的监听,后续还将支持其它功能(比如 转场,滚动优化… …)
OpenAnim.with(this)
.into(view)
.together(
new TranslationXAnimator(500),
new TranslationYAnimator(100),
new RotationXAnimator(0, 360),
new RotationYAnimator(0, 360))
.duration(3000)
.interpolate(new LinearInterpolator())
.repeatCount(ValueAnimator.INFINITE)
.start();
# 配置全局配置
<meta-data
android:name="AnimModule"
android:value="com.open.tvwidget.TestAnimModule" />
# 代码
public class TestAnimModule implements AnimModule {
@Override
public void applyOptions(Context context, AnimConfigBuilder builder) {
// 设置N多种配置(时间,插值器,估值器等等)
builder.duration(1000)
.typeEvaluator(new BounceEaseOut(500))
.interpolate(new DecelerateInterpolator());
}
}
参考资料
属性动画文档地址
属性动画XML
Android 动画 Animator 家族使用指南
估值器详解
属性动画高阶用法
Android动画了解—视图动画 <=上个章节 下个章节=> Android动画了解—转场/过渡(Transition) 动画