时间:2021-05-20
又和大家见面了,这几天一直在忙大创项目,所以没有更新博客,而且我发现看源码这个东西必须写个博客或者笔记啊,这之前一段时机笔者已经看了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方法就好:
我们看一下先进入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的构造方法开始看吧。
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; } }这些代码都能够知道什么意思,这个方法我们只需要注意两点:
这个方法跳过。
然后我们看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:
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方法:
这个方法是给每个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之后
到这里我们就完成了ObjectAnimator对象实例的创建。
到这里创建部分就全部完成了,接下来我们看一下添加属性,这个就很简单了。
添加属性
我们就举一个方法为例吧,拿setInterpolator方法为例:
public void setInterpolator(TimeInterpolator value) { if (value != null) { mInterpolator = value; } else { mInterpolator = new LinearInterpolator(); } }这个方法是我们添加插值器的方法,我们注意到他只是给mInterpolator赋值而已,如果传入为空,则添加线性插值器。
其他的添加属性的方法,像setDuration、setRepeatCount等都是如此,大家下去就自己看一下吧。
由于还不会用 starUML,所以现在还没发画一张时序图(只是手画的,估计大家也不想看哈哈),等学完UML之后会给大家补上的,希望这篇文章大家喜欢。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对的支持。如果你想了解更多相关内容请查看下面相关链接
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
一:想都不用想的,有图有真相,看着爽了,在看下面源码二:实例源码分析①:首先定义接口packagecom.demo.tools.view;/***数字动画自定义
一、源码分析自定义异常数据之前我们先看看一下源码上述代码意思是如果你没有提供就使用springboot提供的类这是springboot提供的异常属性类,我们想要
推荐一个Button弹起PickerView的源码,也可以作为工具类使用。利用inputView做键盘弹起动画。该如何做呢?1.继承UIView2.重写属性&方
上一篇我们说了Android中的tween动画,这一篇我们说说frame动画,frame动画主要是实现了一种类似于gif动画的效果,就是多张图按预先设定好的时间
Android提供三种动画:帧动画、补间动画和属性动画,本篇文章介绍帧动画以及补间动画的使用,属性动画的使用将在后面的文章中分享,那就来复习一下这两种动画的使用