从View#setTranslationX/Y到自定义属性动画
属性动画简单源码解析
在Android Reference中的定义
Sets the vertical/horizontal location of this view relative to its top/left position. This effectively positions the object post-layout, in addition to wherever the object’s layout placed it.
设置此视图相对于其左侧位置的水平位置。 除了View自身的Layout之外,这有效地定位了在Layout后的View。
那这个方法有什么用处呢?我们都知道在属性动画中也有一对transationX/Y,在属性动画中做X/Y方向上的平移动画时是这样的:
1 | ObjectAnimator objectAnimator=ObjectAnimator.ofFloat(btn,"translationX/Y",0f,200f); |
我们从Android源码来看看,动画的每一帧的位置是如何确定的:
首先我们在ObjectAnimation中找到这样一个方法:
1 | /** |
通过注释我们可以知道,这个方法在动画的每一帧被调用,将进度分数通过差值器转化为插值分数,最后再将分数转化为对应的属性动画的数值,再设置给对应的属性。
再通过mValues[i].setAnimatedValue(target);
跳转到下面的函数:
1 | /** |
这是PropertyValuesHolder源码中一段核心的代码,他的主要目的即是,将计算出的当前帧的属性数值赋值到做动画的View中。
我们可以看到这里在拿到当前帧的数值后,通过反射调用mSetter将数值赋值给对应的方法。
1 | /** |
那么mSetter方法是怎么来的呢?我继续往下找,在一个PropertyValuesHolder#setupSetter()
方法中找到了mSetter的赋值逻辑,而setupSetter()
方法又是在PropertyValuesHolder#setupSetterAndGetter()
中被调用的,setupSetterAndGetter()
方法是在ObjectAnimator#initAnimation()
中被调用的。
那么也即是在属性动画的初始化方法中,我们就通过一系列的调用,最终在setupSetter()
方法中拿到了设置对相应的属性值的方法对象.
我们再看setupSetter()
方法中,mSetter是通过一个setupSetterOrGetter()
函数得到的,而且这里的入参prefix
传入了一个”set”。
我们先不管这个”set”是干嘛的,继续往下看:
1 | /** |
这个方法中,首先在propertyMap
中去找之前的缓存,如果没有就通过getPropertyFunction()
方法得到Method对象,并将其放入propertyMap
缓存中。
得,继续看getPropertyFunction()
这个方法:
1 | /** |
我们先看看注释:
JavaBeans约定:名为foo的属性对应setFoo或getFoo这样两个设置和获取属性的方法,用这样的约定来确定setter或getter函数。
此函数指出函数的名称应该是什么,并使用反射在目标对象上查找具有该名称的Method。
再看其中的逻辑
其中的getMethodName()
方法,将属性名的第一位字母大写,然后在前面拼上prefix
前缀,得到最后的符合JavaBeans约定的驼峰方法名,即方法名一定是setXxx()。1
2
3
4
5
6
7
8
9static String getMethodName(String prefix, String propertyName) {
if (propertyName == null || propertyName.length() == 0) {
// shouldn't get here
return prefix;
}
char firstLetter = Character.toUpperCase(propertyName.charAt(0));
String theRest = propertyName.substring(1);
return prefix + firstLetter + theRest;
}
而真正的获取方法对象的逻辑中,通过顺序匹配数值类型来解决了传入的数值类型不匹配的问题。
最后通过Class.getMethod(methodName, args)
方法找到对应的方法对象。其中args是只有一个元素的class数组,所以我们知道ObjectAnimator支持的set方法只能有一个参数。
自定义属性动画
首先新建一个继承自TextView的自定义View, 并提添加一个setXxx的方法,这里我们设置一个给文字设置颜色的方法:1
2
3
4public void setColor(@ColorInt int value){
setTextColor(value);
postInvalidate();
}
在MainActivity中设置自定义的属性动画: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...
private int[] colors;
{
int 赤 = Color.rgb(255, 0, 0);
int 橙 = Color.rgb(255, 165, 0);
int 黄 = Color.rgb(255, 255, 0);
int 绿 = Color.rgb(0, 255, 0);
int 青 = Color.rgb(0, 127, 255);
int 蓝 = Color.rgb(0, 0, 255);
int 紫 = Color.rgb(139, 0,255);
colors = new int[]{赤, 橙, 黄, 绿, 青, 蓝, 紫};
}
...
ObjectAnimator objectAnimator = ObjectAnimator.ofInt(text,"color",colors);
objectAnimator.setRepeatCount(ValueAnimator.INFINITE);
objectAnimator.setRepeatMode(ValueAnimator.REVERSE);
objectAnimator.setDuration(1000);
objectAnimator.setEvaluator(new ArgbEvaluator());
objectAnimator.start();
这里我们用了一个ArgbEvaluator
估值器,他是TypeEvaluator
接口的实现类,专为ARGB颜色计算过渡值。
实现的效果如下: