(现在看不懂没关系,主要是把关系先摆在这)
看到这,会发现其实和补间动画没什么区别,补间动画也能实现这些功能。对补间动画不了解的朋友,可以参考我前一篇文章:补间动画
如果只是要达到上述四种简单效果,用补间动画完全足够了。代码比较简单,依次从左至右的顺序上代码:
//透明度
public void animTest02() {
ObjectAnimator oAnim = ObjectAnimator.ofFloat(myview, "alpha", 1f, 0f, 1f);
oAnim.setDuration(5000);
oAnim.start();
}
//旋转
public void animTest03() {
ObjectAnimator oAnim = ObjectAnimator.ofFloat(myview, "rotation", 0f, 360f);
oAnim.setDuration(5000);
oAnim.start();
}
//平移
public void animTest04() {
ObjectAnimator oAnim = ObjectAnimator.ofFloat(myview, "translationX", 0f, -500f, 0f);
oAnim.setDuration(5000);
oAnim.start();
}
//拉升
public void animTest05() {
ObjectAnimator animator = ObjectAnimator.ofFloat(myview, "scaleY", 1f, 3f, 1f);
animator.setDuration(5000);
animator.start();
}
统一对上述四种动画做个解释,
例如:
ObjectAnimator animator = ObjectAnimator.ofFloat(myview, "scaleY", 1f, 3f, 1f);
利用AS穿透,看源码注释:(此处不涉及深刻探究,仅提供一种读不理解的代码参数的最佳解决方案)
/**
* Constructs and returns an ObjectAnimator that animates between float values. A single
* value implies that that value is the one being animated to. Two values imply starting
* and ending values. More than two values imply a starting value, values to animate through
* along the way, and an ending value (these values will be distributed evenly across
* the duration of the animation).
*
* @param target The object whose property is to be animated. This object should
* have a public method on it called setName()
, where name
is
* the value of the propertyName
parameter.
* @param propertyName The name of the property being animated.
* @param values A set of values that the animation will animate between over time.
* @return An ObjectAnimator object that is set up to animate between the given values.
*/
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
}
翻译如下:
第一个参数myview:是动画操作的目标,而且目标必须有一个对外的公共方法:setXXX方法。(此处XXX就是下面的属性动画名)
第二个参数:属性动画名。
第三个参数:是一个动画变化的值的集合。比如当前1f, 3f, 1f(值按照当前的值转换成百分比计算,例如1f就是100%,3f就是300%)
比如我要对myview(动画操作目标)进行如下操作:
public void animTest06() {
ObjectAnimator animator = ObjectAnimator.ofFloat(myview, "color", 1f, 3f, 1f);
animator.setDuration(5000);
animator.start();
}
目的:本意是,想改变颜色。
效果:点击启动该“动画”的时候,动画不会有任何效果,且logcat会提示这么一条信息:(意思是我的自定义view类的对象myview,当中并没有找到setColor方法)。
反过来看看之前我们设置的四个属性:alpha,rotation,translationX,scaleY。
按照推理去看看我们的自定义view的父类view是否包含了这些个setXXX方法。
操作属性动画,其实就是操作目标target的set+属性名的值的改变。
为了证明确实在target里面有这四个属性:alpha,rotation,translationX,scaleY。
参见view的源码:(其他几个就不再截图了,都有对应的setXXX方法)
开始的时候说过,也就是说如果仅仅对上述四个属性(alpha,rotation,translation,scale)改变,那么百分之95的情况,用补间动画就够了,google为何还要费劲心思写出属性动画来?
主要原因就是不够用。比如上一个例子中说的要对颜色进行动画操作。如果用补间动画就没办法处理了。
解决方案:
之前其实已经说过一个引子。图片如下:
既然说我们没有setColor方法,是不是只需要在自定义view里面加上setColor方法就能对颜色操作了?
答案:(见文章最末尾注释一)翻译源码2: 最佳的属性动画的调用setter方法,用float或者int类型,为动画属性名,写一个返回值为空的setter方法,将导致代码采取这些约束的情况下的优化路径。其他属性类型和返回类型也将工作,但在处理请求由于正常的反射机制将有更多的开销。
还是原来的例子:
在其中加入属性:
private int color;
然后写对应的setter方法:(此处set方法的参数,即我们传递的改变的值,如1f,3f,1f,且必须对应,例如ObjectAnimator.ofFloat对应float f,ObjectAnimator.ofInt对应int型
public void setColor(float f) {
this.setBackgroundColor(Color.RED);//此处一般应写为当前view因外界float f参数改变,而改变的状态,如长度,颜色,数值等等
}
按照此种方式设置后,发现点击后,设置的目标view变红。和我们预设的动画一样。(效果如下)
首先还是看图,之前我已经将ObjectAnimator讲完了,其实大部分功能已经可以实现了。现在讲讲父类ValueAnimator的使用。
ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
anim.setDuration(300);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
final float f = (float) animation.getAnimatedValue();
Log.d("Tag", f + "");
}
});
anim.start();
打印出的log如下图:
可能这个图并不直观,看变化速率。于是我自己手写了一个自定义view用来描点:
onDraw关键代码如下:(大概意思就是通过x轴间距不变(即x轴每次加上10dp),y轴位置乘以100取整描点(即y轴取上面logcat的数据*100取整数部分))
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mdata == null) {
return;
}
//根据数据绘制ui
for (int i = 0; i < mdata.size(); i++) {
x = x + dip2px(context, 10);
y = (int) (mdata.get(i) * 10000 / 100);
canvas.drawCircle(x, dip2px(context, y), 5, p);
}
}
效果图如下:
变化率由此可见:先缓慢再加速再减速。
而属性如何改变,按照程序的思想,肯定是因为数值的改变而设置不同值而达到改变属性值的方式。
2)ValueAnimator的运法:
下图标红其实就是用法总结,将ValueAnimator对象的两个方法成对使用即可。
假如需要实现下图的效果:
代码如下:
ValueAnimator valueAnimator = new ValueAnimator();//实例化
valueAnimator.setDuration(5000);//设置动画时长
valueAnimator.setTarget(id_ball);//设置操作动画的目标
valueAnimator.setObjectValues(new Point(0,0));//设置动画的初始化值,不设置会崩溃。对当前实例其实设置多少,并没太大区别
valueAnimator.setEvaluator(new TypeEvaluator() {
@Override
public Point evaluate(float fraction, Object startValue, Object endValue) {
Log.d("fraction",fraction+"");//打印出fraction变化
Point point = new Point();
point.setX(200*fraction*3);
point.setY(0.5f * 200 * (fraction * 3) * (fraction * 3));
return point;//每次fraction取得一个新的值,会获取一个新的point对象返回
}
});
valueAnimator.start();
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
{
@Override
public void onAnimationUpdate(ValueAnimator animation)
{
Point point = (Point) animation.getAnimatedValue();//取得上面TypeEvaluator返回的对象
id_ball.setX(point.getX());//重设位置
id_ball.setY(point.getY());//重设位置
}
});
注释写的很清楚:
总结一下:
1)设置动画的目标,在addUpdateListener中将对这个目标进行多次(fraction次数)刷新属性(属性例如,位置,颜色等等)
2)设置动画的初始化值。不设置会崩溃,动画开始的时候,会报空指针异常。
3)setEvaluator返回的对象——》addUpdateListener实用的对象 对应
4)fraction也满足上面的折线图的变化,其实涉及插值器。(由于本例5000ms,所以描点会比较密集)
fraction如下:(太多截图不全)
源码1:
参考ObjectAnimator源码
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
}
其中第2行代码调用了构造方法实力化一个ObjectAnimator对象
: private ObjectAnimator(Object target, String propertyName) {
setTarget(target);
setPropertyName(propertyName);
}
里面嵌套两个方法:setTarget(target);
setPropertyName(propertyName);
PS:一般遇到源码开分支这种情况我就开始画图了,毕竟几个圈下来源代码跟下来,估计都能忘记自己最开始是要做什么才看的源代码。
源码2:setTarget(target)源码:(翻译一下注释吧:设置动画的目标object对象的属性,如果动画已经开始,则结束)
/**
* Sets the target object whose property will be animated by this animation. If the
* animator has been started, it will be canceled.
*
* @param target The object being animated
*/
@Override
public void setTarget(@Nullable Object target) {
final Object oldTarget = getTarget();
if (oldTarget != target) {
if (isStarted()) {
cancel();
}
mTarget = target == null ? null : new WeakReference
其他情况都不看了,注释说得比较清楚了。从14行,15行看:
mTarget = target == null ? null : new WeakReference
mInitialized = false;
做了两件事:
1)新建了一个弱引用对象target,给mTarget赋值了(弱引用回收机制这些java的知识,不打算讲,只需要知道实例化了一个对象即可)
2)使mInitialized = false。
仅仅只是赋值的两个操作。第一条分支结束。如下图:
源码3:setPropertyName(propertyName)(核心方法):
翻译1:设置动画的属性名。这个属性名用来倒出一个setter方法(例如setXXX,xxx是属性名,用来设置动画的变化值),例如,一个属性名foo,将调用一个在目标对象上的方法setFoo。如果valuefrom(也就是最开始我们说的1f,3f,1f这些float值)或valueto是null,则调用getter方法(例如getxxx,xxx是属性名)用来推导。
翻译2: 最佳的属性动画的调用setter方法,用float或者int类型,为动画属性名,写一个返回值为空的setter方法,将导致代码采取这些约束的情况下的优化路径。其他属性类型和返回类型也将工作,但在处理请求由于正常的反射机制将有更多的开销。
/**
* Sets the name of the property that will be animated. This name is used to derive
* a setter function that will be called to set animated values.
* For example, a property name of foo
will result
* in a call to the function setFoo()
on the target object. If either
* valueFrom
or valueTo
is null, then a getter function will
* also be derived and called.(见翻译1)
*
* For best performance of the mechanism that calls the setter function determined by the
* name of the property being animated, use float
or int
typed values,
* and make the setter function for those properties have a void
return value. This
* will cause the code to take an optimized path for these constrained circumstances. Other
* property types and return types will work, but will have more overhead in processing
* the requests due to normal reflection mechanisms.
(见翻译2)
*
* Note that the setter function derived from this property name
* must take the same parameter type as the
* valueFrom
and valueTo
properties, otherwise the call to
* the setter function will fail.
*
* If this ObjectAnimator has been set up to animate several properties together,
* using more than one PropertyValuesHolder objects, then setting the propertyName simply
* sets the propertyName in the first of those PropertyValuesHolder objects.
*
* @param propertyName The name of the property being animated. Should not be null.
*/
public void setPropertyName(@NonNull String propertyName) {
// mValues could be null if this is being constructed piecemeal. Just record the
// propertyName to be used later when setValues() is called if so.
if (mValues != null) {
PropertyValuesHolder valuesHolder = mValues[0];
String oldName = valuesHolder.getPropertyName();
valuesHolder.setPropertyName(propertyName);
mValuesMap.remove(oldName);
mValuesMap.put(propertyName, valuesHolder);
}
mPropertyName = propertyName;
// New property/values/target should cause re-initialization prior to starting
mInitialized = false;
}
注释二:属性动画ValueAnimator源码图:( 纯手绘,仅供参考 )