属性动画基础
属性动画及 ValueAnimator 介绍
属性动画介绍
属性动画从 API11
开始提供,动画实现主要依靠 ValueAnimator
和 ObjectAnimator
两个类。其实还有一个 View.animate()
,这个内部原理也是属性动画,而且它已经将常用的动画封装好了,使用起来很方便
属性动画框架继承体系:
1
2
3
4
5
6
Animator
|--ValueAnimator // 不能作用于对象,只能监听后设置
|--ObjectAnimator // 可以设置到对象
|--TimeAnimator
|--AnimatorSet
|--
ValueAnimator
是属性动画中重要且最基本的类,ObjectAnimator
内部也是借助 ValueAnimator 实现的。ValueAnimator 直接子类有两个 ObjectAnimator 和 TimeAnimator。- ValueAnimator 是数值从初始值逐渐变化到结束值,无法直接作用于对象,只能通过设置动画监听,获取动画过程中的过渡值,然后设置对象的属性就可以实现动画。默认插值器为
AccelerateDecelerateInterpolator
,插值器只是动画执行的快慢的控制,控制具体动画过程中获取的值是通过估值器 Evaluator 来实现的。 - ValueAnimator 可以利用 XML 文件生成和 java 代码生成 ValueAnimator 类两种方式实现动画。
ValueAnimator
ValueAnimator 工作流程
ValueAnimator 初始化函数:
1
2
3
4
5
ValueAnimator.ofInt(int … values)//处理整形参数
ValueAnimator.ofFloat(float … values)//处理浮点型
ValueAnimator.ofArgb(int… values) //处理颜色
ValueAnimator.ofObject(TypeEvaluator evaluator, Object… values)// 处理object对象,需要自定义估值器
ValueAnimator.ofPropertyValuesHolder(PropertyValuesHolder… values) // 处理PropertyValuesHolder
ValueAnimator 使用过程:
1
2
3
4
第一步:利用上面的函数生成ValueAnimator对象,
第二步:设置动画的监听, addUpdateListener(ValueAnimator.AnimatorUpdateListener listener)
第三第:四步利用添加的监听函数获取当前动画的值,ValueAnimator.getAnimatedValue()
第四步:设置给View,实现动画
属性动画一般用代码生成(因为属性值无法写死在代码中,一般需要动态获取)。
XML 方式生成属性动画
XML 可以生成属性动画三种标签:
1
2
3
animator:对应ValueAnimator
objectAnimator:对应ObjectAnimator
animatorSet:对应AnimatorSet
animator
定义 XML 文件
1
2
3
4
5
6
7
8
9
10
11
12
13
<animator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueFrom="0" // 初始值
android:valueTo="100" // 结束值
android:valueType="intType" // 变化值类型 :floatType & intType
android:duration="3000" // 动画持续时间(ms),必须设置,动画才有效果
android:startOffset ="1000" // 动画延迟开始时间(ms)
android:fillBefore = “true” // 动画播放完后,视图是否会停留在动画开始的状态,默认为true
android:fillAfter = “false” // 动画播放完后,视图是否会停留在动画结束的状态,优先于fillBefore值,默认为false
android:fillEnabled= “true” // 是否应用fillBefore值,对fillAfter值无影响,默认为true
android:repeatMode= “restart” // 选择重复播放动画模式,restart代表正序重放,reverse代表倒序回放,默认为restart|
android:repeatCount = “0” // 重放次数(所以动画的播放次数=重放次数+1),为infinite时无限重复
android:interpolator = @[package:]anim/interpolator_resource // 插值器,即影响动画的播放速度
/>
API23 之后还可以利用 PropertyValuesHolder
和 keyframe
实现
1
2
3
4
5
6
7
8
9
10
<animator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:repeatCount="1"
android:repeatMode="reverse">
<propertyValuesHolder>
<keyframe android:fraction="0" android:value="1"/>
<keyframe android:fraction=".2" android:value=".4"/>
<keyframe android:fraction="1" android:value="0"/>
</propertyValuesHolder>
</animator>
objectAnimator
1
2
3
4
5
6
7
< objectAnimator
android:duration="5000"
android:propertyName="trimPathStart"
android:repeatCount="infinite"
android:repeatMode="restart"
android:valueFrom="1"
android:valueTo="0"/>
利用 AnimatorInflater 加载上面定义的 xml 文件,生成 Animator 实例
1
2
3
4
5
6
7
val animator: ValueAnimator = (AnimatorInflater.loadAnimator(this, R.animator.animator) as ValueAnimator)
animator.addUpdateListener { animation ->
val value = animation?.animatedValue as Float
LogUtil.i("hacket","addUpdateListener")
anim_view.alpha = value
}
animator.start()
ValueAnimator 代码方式详解
属性动画最好用代码实现。ValueAnimator
无法像 ObjectAnimator
一样直接作用于对象,只能通过添加监听,获取动画过程之,然后手动设置给对象改变对象的属性。
ValueAnimator.ofInt(int … values)
values 可以有多个值,ofInt 作用是从初始值(参数中的第一个)以整数形式过渡到结束值,如果参数有多个,那就是从初始值过渡到第二个参数,然后从第二个参数过渡到第三个参数,后面以此类推。
1
2
3
4
5
6
7
8
9
10
11
btn_ValueAnimator_ofInt.setOnClickListener {
val valueAnimator = ValueAnimator.ofInt(1, 10)
valueAnimator.cancel()
valueAnimator.duration = 1000
valueAnimator.addUpdateListener { animation ->
val data = animation?.animatedValue as Int
Log.i("hacket", "getAnimatedValue:$data")
}
valueAnimator.addListener(object : AnimatorListenerAdapter(){})
valueAnimator.start()
}
输出结果:
ofInt 函数,获取到的值都是整形。
1
2
3
4
5
6
7
8
9
10
getAnimatedValue=1
getAnimatedValue=1
getAnimatedValue=1
getAnimatedValue=2
getAnimatedValue=4
getAnimatedValue=6
getAnimatedValue=8
getAnimatedValue=9
getAnimatedValue=9
getAnimatedValue=10
ValueAnimator.ofFloat
ofFloat 和 onInt 用法相同,只是数值精度不同
1
2
3
4
5
6
7
8
9
10
11
12
valueAnimator = ValueAnimator.ofFloat(1,0.5f);
valueAnimator.setDuration(1000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float data = (float) animation.getAnimatedValue();
Matrix matrix = new Matrix();
matrix.setScale(data,data);
//ImageView要支持matrix,需要设置ImageView的ScaleType为matrix
imageView.setImageMatrix(matrix);
}
});
ValueAnimator.ofArgb 颜色渐变
ofArgb 是 api21 提供的新方法,可以帮助我们实现颜色的渐变。
1
2
3
4
5
6
public static ValueAnimator ofArgb(int... values) {
ValueAnimator anim = new ValueAnimator();
anim.setIntValues(values);
anim.setEvaluator(ArgbEvaluator.getInstance());
return anim;
}
ofArgb 内部利用 ArgbEvaluator 估值器计算动画运行期间的过渡颜色,所以颜色过渡的算法一定在 ArgbEvaluator 的 evaluate 方法中。
示例:
1
2
3
4
5
6
7
8
9
10
valueAnimator = ValueAnimator.ofArgb(Color.RED, Color.GREEN);
valueAnimator.setDuration(3000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int data = (int) animation.getAnimatedValue();
imageView.setBackgroundColor(data);
textView.setBackgroundColor(data);
}
});
ValueAnimator.ofObject()
其实 ValueAnimator.ofObject()的本质还是操作 值,只是是采用将 多个值 封装到一个对象里的方式 同时对多个值一起操作而已
方法:
1
ofObject(TypeEvaluator evaluator, Object… values)
参数说明:
1
2
evaluator:自定义估值器
values:开始结束对象,ofObject处理对象,需要传入自定义估值器,告诉系统如何计算动画运行过程中的值。
1
2
3
4
5
6
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values) {
ValueAnimator anim = new ValueAnimator();
anim.setObjectValues(values);
anim.setEvaluator(evaluator);
return anim;
}
需要自定义估值器,内部会设置自定义的估值器。
实现的是一个从左上角到右下角的坐标过渡:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
btn_ValueAnimator_ofObjject.setOnClickListener {
val valueAnimator = ValueAnimator.ofObject(PointTypeEvaluator(), PointF(0F, 0F), PointF(500F, 200F))
valueAnimator.duration = 5000
valueAnimator.addUpdateListener { animation ->
val point = animation?.animatedValue as PointF
anim_view3.translationX = point.x
anim_view3.translationY = point.y
tv_status3.text = "addUpdateListener point=$point"
Log.i("hacket", "addUpdateListener value=$point")
}
valueAnimator.start()
}
class PointTypeEvaluator : TypeEvaluator<PointF> {
override fun evaluate(fraction: Float, startValue: PointF?, endValue: PointF?): PointF {
val startP = startValue!!
val endP = endValue!!
val x = startP.x + fraction * (endP.x - startP.x)
val y = startP.y + fraction * (endP.y - startP.y)
return PointF(x, y)
}
}
ValueAnimator.ofPropertyValuesHolder
PropertyValuesHolder 类:
这个类持有一个属性名和对应的多个属性值,动画运行过程中会返回这种类型。ValueAnimator.ofInt(),ValueAnimator.ofFloat() 等所有的函数内部都是把值存储到 PropertyValuesHolder 中。
用途,同时改变多个属性时用这个。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
val floatArrayOf = floatArrayOf(1f, 2f, 3f)
val floatArrayOf2 = floatArrayOf(4f, 5f, 6f)
val propertyValuesHolder1 = PropertyValuesHolder.ofFloat("str1", *floatArrayOf)
val propertyValuesHolder2 = PropertyValuesHolder.ofFloat("str2", *floatArrayOf2)
val valueAnimator = ValueAnimator.ofPropertyValuesHolder(propertyValuesHolder1, propertyValuesHolder2)
valueAnimator.duration = 1000
valueAnimator.interpolator = LinearInterpolator()
valueAnimator.addUpdateListener { animation ->
val name = animation.getAnimatedValue("str1") as Float
val age = animation.getAnimatedValue("str2") as Float
LogUtil.i("$name $age")
}
valueAnimator.start()
Animation.getAnimatedValue("propertyName")
就可以获取到对应的值。
PropertyValuesHolder 的 ofXX 函数比较多:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ofFloat(Property<?, Float> property, float... values)
ofFloat(String propertyName, float... values)
ofInt(String propertyName, int... values)
ofInt(Property<?, Integer> property, int... values)
ofKeyframe(String propertyName, Keyframe... values)
ofKeyframe(Property property, Keyframe... values)
ofMultiFloat(String propertyName, float[][] values)
ofMultiFloat(String propertyName, TypeConverter<V, float[]> converter, TypeEvaluator<V> evaluator, V... values)
ofMultiFloat(String propertyName, Path path)
ofMultiFloat(String propertyName, TypeConverter<T, float[]> converter, TypeEvaluator<T> evaluator, Keyframe... values)
ofMultiInt(String propertyName, TypeConverter<V, int[]> converter, TypeEvaluator<V> evaluator, V... values)
ofObject(String propertyName, TypeConverter<PointF, ?> converter, Path path)
ofObject(String propertyName, TypeEvaluator evaluator, Object... values)
ofObject(Property<?, V> property, TypeConverter<T, V> converter, TypeEvaluator<T> evaluator, T... values)
// 。。。。。。
Interpolator 插值器 动画完成速度模型(时间完成度转换成动画完成度)
插值器继承结构
1
2
3
4
TimeInterpolator
Interpolator
BaseInterpolator
具体的插值器(类似LinearInterpolator)
补间动画实现 Interpolator
接口;属性动画实现 TimeInterpolator
接口,TimeInterpolator 接口是属性动画中新增的,Interpolator 老接口继承了 TimeInterpolator,这使得所有过去的 Interpolator 实现类都可以直接在**属性动画**
使用。所以现在的插值器直接实现 Interpolator 或者 TimeInterpolator 都是一样的。
1
2
3
4
5
6
public interface Interpolator extends TimeInterpolator {
}
public interface TimeInterpolator {
float getInterpolation(float input);
}
getInterpolation 函数
TimeInterpolator 中的 getInterpolation(float input) 函数。
参数 input: input 的范围在 [0,1.0] 之间,代表当前动画所在的点(执行了多长时间,动画的进度),0 代表动画开始,1.0 代表动画完成。相当于动画执行的时间轴。
getInterpolation() 返回值: 返回值代表动画的完成程度(0 代表动画刚开始的状态,0.5 代表动画完成一半的状态,1 代表动画完成的状态),这个返回值可以超过 1.0 或者小于 0,但是超过 1 或者小于 0 没有意义。所以就代表如果 input=0 表示动画刚开始,但是如果此时返回值为 1,动画也是完成状态,View 的形态会是动画完成后的位置和状态。
系统提供的 LinearInterpolator 实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
public LinearInterpolator() {
}
public LinearInterpolator(Context context, AttributeSet attrs) {
}
// 直接返回input,说明默认动画进度和动画完成状态是线性变化的。
public float getInterpolation(float input) {
return input;
}
/** @hide */
@Override
public long createNativeInterpolator() {
return NativeInterpolatorFactoryHelper.createLinearInterpolator();
}
}
系统提供的 AccelerateInterpolator 插值器
动画开始变化缓慢,后面越来越快。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class AccelerateInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
private final float mFactor;
private final double mDoubleFactor;
public AccelerateInterpolator() {
mFactor = 1.0f;
mDoubleFactor = 2.0;
}
public AccelerateInterpolator(float factor) {
mFactor = factor;
mDoubleFactor = 2 * mFactor;
}
// 重点,如果按照默认值,动画的完成状态的变化是动画进度变化(运行时间)的平方。Input=0.1 返回0.01,input=0.9 返回0.81,input=1 返回1
public float getInterpolation(float input) {
if (mFactor == 1.0f) {
return input * input;
} else {
return (float)Math.pow(input, mDoubleFactor);
}
}
}
如果 mFactor == 1.0f 时 AccelerateInterpolator 的效果曲线,X 轴为时间轴,Y 轴为动画完成状态 :
所以自定义插值器主要是根据 getInterpolation(float input) 函数中的 input,得出对应的动画状态完成进度。
自定义 interpolator
- 第一步自定义类可以实现 Interpolator 或 TimeInterpolator 接口,如果要利用 BaseInterpolator,需要 api22,而且还需要实现 implements NativeInterpolatorFactory 接口。
- 第二步然后重写 getInterpolation 方法,在 getInterpolation 内部利用参数 input 得到对应的动画完成进度,并返回,就完成了插值器的自定义。
案例
1
2
3
4
5
6
7
8
9
10
public class InterpolationDemo implements Interpolator{
@Override
public float getInterpolation(float input) {
if (input > 0 && input < 0.5){
return input /10;
}else{
return input * input;
}
}
}
1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXDelta="-300"
android:toXDelta="500"
android:fromYDelta="-300"
android:toYDelta="1000"
android:duration="3000"
android:fillBefore="true"/>
1
2
3
InterpolationDemo interpolationDemo = new InterpolationDemo();
mTranslateAnimation.setInterpolator(interpolationDemo);
imageView.startAnimation(mTranslateAnimation);
TypeEvaluator 估值器 (动画完成度对应的具体值)
作用:设置动画 如何从初始值 过渡到 结束值 的逻辑
系统估值器
IntEvaluator
1
2
3
4
5
6
public class IntEvaluator implements TypeEvaluator<Integer> {
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int)(startInt + fraction * (endValue - startInt));
}
}
- 参数
实现 TypeEvaluator,实现了 evaluate 函数,evaluate 三个参数的意义:
1
2
3
fraction:动画运行了多久,[0-1]的规范化数据,如果设置duration为1000ms,达到100ms时,fraction值为0.1,200ms为0.2。
startvalue:开始变化的值,
endValue:变化结束的值。
- 返回值
TypeEvaluator 的 evaluate 函数返回值为 (int)(startInt + fraction * (endValue - startInt)),
很简单就是开始值加上动画运行的时间乘以(结束值减去开始值)。
FloatEvaluator
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class FloatEvaluator implements TypeEvaluator {
// FloatEvaluator实现了TypeEvaluator接口
// 重写evaluate()
public Object evaluate(float fraction, Object startValue, Object endValue) {
// 参数说明
// fraction:表示动画完成度(根据它来计算当前动画的值)
// startValue、endValue:动画的初始值和结束值
float startFloat = ((Number) startValue).floatValue();
return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
// 初始值 过渡 到结束值 的算法是:
// 1. 用结束值减去初始值,算出它们之间的差值
// 2. 用上述差值乘以fraction系数
// 3. 再加上初始值,就得到当前动画的值
}
}
PointFEvaluator api21
估值器 TypeEvaluator 和插值器 TimeInterpolator 区别?
作用
- 插值器(TimeInterpolator)决定 值 的变化模式(匀速、加速等)
- 估值器(TypeEvaluator)决定 值 的具体变化数值
在哪用?
- 插值器(TimeInterpolator)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ValueAnimator#animateValue Android29
void animateValue(float fraction) {
fraction = mInterpolator.getInterpolation(fraction);
mCurrentFraction = fraction;
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].calculateValue(fraction);
}
if (mUpdateListeners != null) {
int numListeners = mUpdateListeners.size();
for (int i = 0; i < numListeners; ++i) {
mUpdateListeners.get(i).onAnimationUpdate(this);
}
}
}
插值器在需要更新值时,讲当前线性消逝的 fraction 转换成你想要的的 fraction,用来定义动画的变化速率
- 估值器(TypeEvaluator)
计算当前动画进度的值用到,PropertyValuesHolder#calculateValue 时调用 KeyframeSet#getValue(fraction),里面会用到 TypeEvaluator 动画更新的实际值
自定义估值器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
btn_ValueAnimator_ofObject.setOnClickListener {
val heightAndColor1 = HeightAndColorEvaluator.HeightAndColor()
heightAndColor1.height = 200
heightAndColor1.color = Color.RED
val heightAndColor2 = HeightAndColorEvaluator.HeightAndColor()
heightAndColor2.height = 400
heightAndColor2.color = Color.GREEN
val valueAnimator = ValueAnimator.ofObject(HeightAndColorEvaluator(), heightAndColor1, heightAndColor2)
valueAnimator.duration = 3000
valueAnimator.addUpdateListener { animation ->
val data = animation.animatedValue as HeightAndColorEvaluator.HeightAndColor
anim_view.setBackgroundColor(data.color)
val lp = anim_view.layoutParams
lp.height = data.height
anim_view.layoutParams = lp
}
valueAnimator.start()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
public class HeightAndColorEvaluator implements TypeEvaluator<HeightAndColorEvaluator.HeightAndColor> {
@Override
public HeightAndColor evaluate(float fraction, HeightAndColor startValue, HeightAndColor endValue) {
int startHeight = startValue.getHeight();
int currHeight = (int) (startHeight + fraction * (endValue.getHeight() - startHeight));
int currColor = getCurrRGBA(fraction, startValue.getColor(), endValue.getColor());
HeightAndColor heightAndColor = new HeightAndColor();
heightAndColor.setColor(currColor);
heightAndColor.setHeight(currHeight);
return heightAndColor;
}
public int getCurrRGBA(float fraction, int startValue, int endValue) {
int startInt = startValue;
float startA = ((startInt >> 24) & 0xff) / 255.0f;
float startR = ((startInt >> 16) & 0xff) / 255.0f;
float startG = ((startInt >> 8) & 0xff) / 255.0f;
float startB = (startInt & 0xff) / 255.0f;
int endInt = endValue;
float endA = ((endInt >> 24) & 0xff) / 255.0f;
float endR = ((endInt >> 16) & 0xff) / 255.0f;
float endG = ((endInt >> 8) & 0xff) / 255.0f;
float endB = (endInt & 0xff) / 255.0f;
// convert from sRGB to linear
startR = (float) Math.pow(startR, 2.2);
startG = (float) Math.pow(startG, 2.2);
startB = (float) Math.pow(startB, 2.2);
endR = (float) Math.pow(endR, 2.2);
endG = (float) Math.pow(endG, 2.2);
endB = (float) Math.pow(endB, 2.2);
// compute the interpolated color in linear space
float a = startA + fraction * (endA - startA);
float r = startR + fraction * (endR - startR);
float g = startG + fraction * (endG - startG);
float b = startB + fraction * (endB - startB);
// convert back to sRGB in the [0..255] range
a = a * 255.0f;
r = (float) Math.pow(r, 1.0 / 2.2) * 255.0f;
g = (float) Math.pow(g, 1.0 / 2.2) * 255.0f;
b = (float) Math.pow(b, 1.0 / 2.2) * 255.0f;
return Math.round(a) << 24 | Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b);
}
public static class HeightAndColor {
private int color;
private int height;
public int getColor() {
return color;
}
public void setColor(int color) {
this.color = color;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
}
}
#
#