ObjectAnimator属性动画源码分析篇
又和大家见面了,这几天一直在忙大创项目,所以没有更新博客,而且我发现看源码这个东西必须写个博客或者笔记啊,这之前一段时机笔者已经看了valueanimator和objectanimator的源码了,但是这才过了几天,搞了会别的事情就忘得几乎一干二净了。现在又要重头看一遍很痛苦额-。+。
另外,笔者已经在简书写了关于属性动画的比较系统的详细的文章,之后会陆续在csdn上重新写的(是重新写,不是复制过去哦,因为第一次写的实在是太烂了-。=)
好了不继续扯皮了,我们看来一下今天想要讲的东西——objectanimator的源码分析(使用部分)。
objectanimator使用部分源码
我们都知道属性动画使用分为三部分:创建、添加属性、启动。而我们今天要讲的就是关于创建和添加属性。首先来看创建的源码吧:
创建
首先看一下今天所有用到的背景:
写了一个自定义的view——pointview,用来实现一个小球的移动效果,pointview代码如下(可以不用看-。+):
public class pointview extends view { private point mcurrentpoint; private paint paint; /** * 两个构造方法 **/ public pointview(context context) { super(context); paint = new paint(paint.anti_alias_flag); paint.setcolor(color.cyan); paint.setstrokewidth(5); paint.setstyle(paint.style.stroke); } public pointview(context context, @nullable attributeset attrs) { super(context, attrs); paint = new paint(paint.anti_alias_flag); paint.setcolor(color.cyan); paint.setstrokewidth(5); paint.setstyle(paint.style.stroke); } /** * ondraw开始使用画笔,如果mcurrentpoint为空,就创建point对象, * 否则就直接调用drawpoint方法 **/ @override protected void ondraw(canvas canvas) { super.ondraw(canvas); if (mcurrentpoint == null) { mcurrentpoint = new point(500, 500); drawpoint(canvas); } else { drawpoint(canvas); } } //设置一个属性添加的方法 public void setmove(point point) { mcurrentpoint = point; invalidate(); } //启动动画 private void startanimation() { objectanimator animator = objectanimator.ofobject(pointview.this, "move", new ballevaluator(), new point(500, 500), new point(500, 800), new point(700, 800)); animator.setinterpolator(new linearinterpolator()); animator.setduration(5000); animator.start(); } //在外部调用的方法,通过此方法,开启小球动作的动画。 public void moveball() { startanimation(); } private void drawpoint(canvas canvas) { canvas.drawcircle(mcurrentpoint.getx(), mcurrentpoint.gety(), 30, paint); } //小球对象的估值器 public class ballevaluator implements typeevaluator<point> { float x; float y; @override public point evaluate(float fraction, point startvalue, point endvalue) { float startx = startvalue.getx(); float starty = startvalue.gety(); float endx = endvalue.getx(); float endy = endvalue.gety(); x = fraction * (endx - startx); y = fraction * (endy - starty); log.e("asdsad", "x = " + x + "y = " + y); return new point(startx + x, starty + y); } } }
代码可能有点多,不过这不是主要的,我们今天关注的只是属性动画,所以我们只需要看里面的startanimation方法和setmove方法就好:
- setmove:由于我们知道属性动画objectanimator类是通过将propertyname拼接成对应的set方法,然后通过反射机制去调用该方法,所以我们需要有一个对应的set方法。
- startanimation:这个方法我们用来设置我们的动画以及启动动画。setmove方法,很简单,我们只是将传入的新的小球对象赋值给了mcurrentball,然后调用invalidate方法重新绘制。下面看一下startanimation方法:
objectanimator animator = objectanimator.ofobject(pointview.this, "move", new ballevaluator(), new point(500, 500), new point(500, 800), new point(700, 800)); animator.setinterpolator(new linearinterpolator()); animator.setduration(5000); animator.start();
我们看一下先进入ofobject方法(相信ofobject里面的参数大家都看得懂):
public static objectanimator ofobject(object target, string propertyname, typeevaluator evaluator, object... values) { objectanimator anim = new objectanimator(target, propertyname); anim.setobjectvalues(values); anim.setevaluator(evaluator); return anim; }
我们发现ofobject是一个静态方法:他在里面创建了一个objectanimator对象。然后调用了setobjeectvalues和setevaluator方法,分别添加了数据和估值器。
也就是这个ofobject里面有三个入口等着我们进去:
- objectanimator的构造方法
- setobjectvalues方法
- setevaluator方法
那我们先从objectanimator的构造方法开始看吧。
objectanimator构造方法:
private objectanimator(object target, string propertyname) { settarget(target); setpropertyname(propertyname); }
又是两个方法,一个一个去看,先进入settarget:
@override public void settarget(@nullable object target) { final object oldtarget = gettarget(); if (oldtarget != target) { if (isstarted()) { cancel(); } mtarget = target == null ? null : new weakreference<object>(target); // new target should cause re-initialization prior to starting minitialized = false; } }
这些代码都能够知道什么意思,这个方法我们只需要注意两点:
- 我们将传入的target传给了mtarget这个弱引用。
- minitialized = false;这个属性我们可以这么理解,在他的objectanimator没有准备就绪(初始化过程尚未完成时),他一直都是false。
这个方法跳过。
然后我们看setpropertyname方法:
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; }
首先介绍一下mvalues和mvaluesmap这两个属性,他们都是存储propertyvalueholder的属性,而且储存的都一样,只是mvaluesmap可以让我们通过propertyname来查找对应的propertyvalueholder。
propertyvaluesholder[] mvalues; hashmap<string, propertyvaluesholder> mvaluesmap;
这个方法只是将propertyname放入propertyvalueholder中(具体逻辑如上,先判断mvalues是否为空,如果不为空就将propertyname放入mvalues和mvaluesmap中,最后将propertyname赋值给mpropertyname),可以过了。
现在我们的objectanimator构造方法看完了,我们接着看setobjectvalues方法:
anim.setobjectvalues:
@override public void setobjectvalues(object... values) { if (mvalues == null || mvalues.length == 0) { // no values yet - this animator is being constructed piecemeal. init the values with // whatever the current propertyname is if (mproperty != null) { setvalues(propertyvaluesholder.ofobject(mproperty, (typeevaluator) null, values)); } else { setvalues(propertyvaluesholder.ofobject(mpropertyname, (typeevaluator) null, values)); } } else { super.setobjectvalues(values); } }
这段代码的总体逻辑只有一个:如果mvalues没有值,那么就调用setvalues方法,否则就调用父类的setobjectvalues方法。
感觉很乱啊,稳住! 我们这是第一次创建的对象,所以肯定是为空的,所以我们只需要看setvalues方法就好了,但是注意,这里还有propertyvalueholder,所以我们决定先看一下propertyvalueholder的ofobject方法:
propertyvalueholder.ofobject:
public static <v> propertyvaluesholder ofobject(property property, typeevaluator<v> evaluator, v... values) { propertyvaluesholder pvh = new propertyvaluesholder(property); pvh.setobjectvalues(values); pvh.setevaluator(evaluator); return pvh; }
跟上面objectanimator的ofobject差异不多,我们就不多说了,有两条路可以选:
- setobjectvalues
- setevaluator
先看setobjectvalues:
propertyvalueholder.setobjectvalues:
public void setobjectvalues(object... values) { mvaluetype = values[0].getclass(); mkeyframes = keyframeset.ofobject(values); if (mevaluator != null) { mkeyframes.setevaluator(mevaluator); } }
我知道大家都快吐了,现在keyframes又出来了,头皮发麻对吧,稳住,我们坚持住!
这个keyframes是keyframeset的接口,我们看一下keyframeset的ofobject方法:
keyframeset.ofobject方法:
public static keyframeset ofobject(object... values) { int numkeyframes = values.length; objectkeyframe keyframes[] = new objectkeyframe[math.max(numkeyframes,2)];//创建了一个至少为两位的objectkeyframe对象 if (numkeyframes == 1) { keyframes[0] = (objectkeyframe) keyframe.ofobject(0f); keyframes[1] = (objectkeyframe) keyframe.ofobject(1f, values[0]); } else { keyframes[0] = (objectkeyframe) keyframe.ofobject(0f, values[0]); for (int i = 1; i < numkeyframes; ++i) { keyframes[i] = (objectkeyframe) keyframe.ofobject((float) i / (numkeyframes - 1), values[i]); } } return new keyframeset(keyframes); }
我们终于看到了一个比较熟悉的类,keyframe,这个叫做关键帧的类终于出现了,我们简单分析一下这个方法:
首先创建了一个至少为两位的objectkeyframe对象,然后对values的长度进行判断,如果只有一个值,那么就将唯一的一个值添加到最后一位(此时也就是第二位),否则就依次添加。最后将objectkeyframe的数组转换成keyframeset类型返回。
现在我们回到propertyvalueholder的setobjectvalues方法中,接下来我们要看一下setevaluator方法(需要在keyframeset中查看)
keyframeset.setevaluator方法:
public void setevaluator(typeevaluator evaluator) { mevaluator = evaluator; }
这个不用多说,直接过。
现在我们的propertyvalueholder的ofobject方法已经看完了,我们跳回anim.setobjectvalues方法,看一下setvalues方法:
setvalues:
public void setvalues(propertyvaluesholder... values) { int numvalues = values.length; mvalues = values; mvaluesmap = new hashmap<string, propertyvaluesholder>(numvalues); for (int i = 0; i < numvalues; ++i) { propertyvaluesholder valuesholder = values[i]; mvaluesmap.put(valuesholder.getpropertyname(), valuesholder); } // new property/values/target should cause re-initialization prior to starting minitialized = false; }
这个完全就是我们刚才说的储存propertyvalueholder的两个属性mvalues和mvaluesmap添加数据的过程,过。
现在我们只差最后一个大方法了,anim.setevaluator了,激动!!!
anim.setevaluator方法:
public void setevaluator(typeevaluator value) { if (value != null && mvalues != null && mvalues.length > 0) { mvalues[0].setevaluator(value); } }
这个方法是给每个propertyvalueholder对象都执行setevaluator方法,我们点进去这个方法看一下:
propertyvalueholder.setevaluator方法:
public void setevaluator(typeevaluator evaluator) { mevaluator = evaluator; mkeyframes.setevaluator(evaluator); }
又进入了keyframes的setevaluator,我们接着看一下keyframeset的setevaluator方法:
keyframeset.setevaluator方法:
public void setevaluator(typeevaluator evaluator) { mevaluator = evaluator; }
这个方法不用说了。
最后我们返回了创建的anim的对象,到现在为止,我们得到我们想要的objectanimator对象了。
这个过程是有点繁琐,我们现在屡一下思路:
调用了objectanimator.ofobject之后
- 首先new一个objectanimator对象,进入objectanimator的构造方法中:在构造方法中,我们执行了两个方法:settarget和setpropertyname。
- 然后调用了objectanimator的setobjectvalues方法:在这个方法中我们首先实例化了propertyvalueholder对象,然后调用setvalues方法将propertyvalueholder传入。
- 之后调用了objectanimator的setevaluator方法:添加了估值器。
- 最后返回了objectanimator对象。
到这里我们就完成了objectanimator对象实例的创建。
到这里创建部分就全部完成了,接下来我们看一下添加属性,这个就很简单了。
添加属性
我们就举一个方法为例吧,拿setinterpolator方法为例:
public void setinterpolator(timeinterpolator value) { if (value != null) { minterpolator = value; } else { minterpolator = new linearinterpolator(); } }
这个方法是我们添加插值器的方法,我们注意到他只是给minterpolator赋值而已,如果传入为空,则添加线性插值器。
其他的添加属性的方法,像setduration、setrepeatcount等都是如此,大家下去就自己看一下吧。
由于还不会用 staruml,所以现在还没发画一张时序图(只是手画的,估计大家也不想看哈哈),等学完uml之后会给大家补上的,希望这篇文章大家喜欢。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对的支持。如果你想了解更多相关内容请查看下面相关链接
推荐阅读
-
Java之HashMap源码分析(第五篇:访问元素)
-
Android属性动画Property Animation系列一之ObjectAnimator_html/css_WEB-ITnose
-
Android源码解析之属性动画详解
-
Android源码解析之属性动画详解
-
vuex源码分析(二) state及strict属性 详解
-
Vue.js 源码分析(二十二) 指令篇 v-model指令详解
-
Android源码—属性动画的工作原理
-
jQuery 源码分析(十三) 数据操作模块 DOM属性 详解
-
Vue.js 源码分析(十六) 指令篇 v-on指令详解
-
别翻了,这篇文章绝对让你深刻理解java类的加载以及ClassLoader源码分析【JVM篇二】