所以需要对这些方法进行封装
原因:动画是需要时间开销才能完成的。如果不给出关键帧动画,动画过程将无法控制。在不同的时间点
空间的状态也不一样。
例如: 让一个view在3s中实现放大两倍后再回到初始大小
在ofFloat中传入三个参数:
第一个参数是我们的view
第二个是我们要执行的属性,scaleX为X轴缩放,这里的值不能随便填,但是S的大小写是可以填的 ,因为最后是通过反射去获 设置view的属性,在源码中,有将方法的第一个字母变成大写。
第三个参数是不定值,第一个1f表示view初始大小,假设为100px,第二个2f为放大两倍为200px,第三个1f为在200px的基础上缩小为100px,跟前一个参数有关系
那么执行完以后为什么能够达到这样的放大缩小的效果,这些1f,2f,1f在底层是怎么体现的呢,在这3s钟内是如何分配放大缩小呢?差值器是什么意思?带着这些疑问,手撸开始。(动画绘制不能超过16毫米==阅读源码发现)
//代码 :https://github.com/bbe-wang/NetEaseEvent ==一个类似源码的简易框架
动画效果
拿到代码再开始分析吧,直接看很蒙圈=_=
下面是一个放大放大动画的管理框架,通过分析我们知道 动画从上层调用开始 也就是setScaleX开始,就开始、==
从动画的ofFloat开始,
需要重写自定义动画,因为里面的所有引用全部需要用自己的,所以这个自定义动画里面基本需要四个方法:
offLoat(),
setInterpolator(),
setDuration(),
start()
========
package com.asyn.task.library;
import android.animation.ObjectAnimator;
import android.view.View;
import java.lang.ref.WeakReference;
/**
* Created by ThinkPad on 2019/11/22.
*/
public class MyObjectAnimator implements VSYNCManager.AnimationFrameCallback{
private static final String LOG_TAG = "MyObjectAnimator";
long mStartTime =-1;//动画开始时间
private long mDuration =0;
private WeakReference mTarget;
/**
*
* @param target 需要执行动画的view
* @param propertyName 执行动画的名字
* @param values 关键帧
* @return
*/
//属性管理器
MyPropertyValuesHodler myPropertyValuesHodler;
private float index = 0;
private TimeInterpolator interpolator;
public MyObjectAnimator(View target, String propertyName, float[] values) {
mTarget = new WeakReference(target);
myPropertyValuesHodler = new MyPropertyValuesHodler(propertyName,values);
}
public void setDuration(long mDuration) {
this.mDuration = mDuration;
}
public void setInterpolator(TimeInterpolator interpolator) {
this.interpolator = interpolator;
}
public static MyObjectAnimator ofFloat(View target, String propertyName, float... values) {
MyObjectAnimator animator = new MyObjectAnimator(target,propertyName,values);
return animator;
}
/**
* 每隔16毫米调用
* @param currntTime
* @return
*/
@Override
public boolean doAnimationFrame(long currntTime) {
//需要计算当前的百分比
float total = mDuration /16;//一共分成多少分
//动画执行的百分比(index ++ )/total
float fraction =(index ++ )/ total;
if (interpolator != null){
fraction = interpolator.getInterpolator(fraction);
}
if (index >= total){
index = 0;
}
myPropertyValuesHodler.setAnimatedValue(mTarget.get(),fraction);
return false;
}
public void start(){
myPropertyValuesHodler .setupSetter();
mStartTime =System.currentTimeMillis();
VSYNCManager.getInstance().add(this);
}
}
其他的跟我们之前看的差不多,然后找到了ProPertyVauwaHodler //属性管理器 和VSYNCManager
package com.asyn.task.library;
import android.view.View;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 关键帧管理类
*/
public class MyPropertyValuesHodler {
//属性名字
String mProperName;
Class mValueType;
//反射
Method mSeeter = null;
//关键帧管理类
MyKeyFrameSet myKeyFrameSet;
public MyPropertyValuesHodler(String propertyName ,float... values){
this.mProperName = propertyName;
mValueType =float.class;
myKeyFrameSet =MyKeyFrameSet.ofFloat(values);
}
public void setupSetter(){
char fierstLetter =Character.toUpperCase(mProperName.charAt(0));
String theRest =mProperName.substring(1);
String methodName ="set"+fierstLetter+theRest;
//反射
try {
mSeeter = View.class.getMethod(methodName,float.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
/**
* 通过反射方式进行属性赋值
* @param viw
* @param fraction
*/
public void setAnimatedValue (View viw ,float fraction){
Object values=myKeyFrameSet.getValues(fraction);
//通过反射方法进行赋值操作
try {
mSeeter.invoke(viw,values);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
只从start看,ProPertyVauwaHodler有两个比较重要的方法,
setupSetter===通过MainActivity的ScaleX 方法即:mProperName 拿到Method的反射类
setAnimatedValue:通过反射方式进行属性赋值
package com.asyn.task.library;
import java.util.ArrayList;
import java.util.List;
/**
* Created by ThinkPad on 2019/11/22.
*/
public class VSYNCManager {
private static final VSYNCManager ourInstance =new VSYNCManager();
private List list =new ArrayList<>();
public static VSYNCManager getInstance(){
return ourInstance;
}
private VSYNCManager(){
new Thread(runnable).start();
}
Runnable runnable =new Runnable() {
@Override
public void run() {
//循环执行
while (true) {
try {
Thread.sleep(16);
} catch (Exception e) {
e.printStackTrace();
}
for (AnimationFrameCallback animationFrameCallback : list) {
animationFrameCallback.doAnimationFrame(System.currentTimeMillis());
}
}
}
};
interface AnimationFrameCallback{
boolean doAnimationFrame(long currntTime);
}
public void add(AnimationFrameCallback animationFrameCallback){
list.add(animationFrameCallback);
}
}
里面只有一个方法,就是启动了一个线程,通过接口AnimationFrameCallback,MyObjectAnimator实现了这个接口,他将这个动画添加到集合中,再每隔16秒循环执行doAnimationFrame方法,将设置的总时长,这次设置的是3000,分成16份, 通过计算将动画的百分比和软引用中的view传递给
这个方法中 ProPertyVauwaHodler的setAnimatedValue方法,通过反射对view的属性进行赋值
其中关键帧的对象初始化在下面
package com.asyn.task.library;
/**
* Created by ThinkPad on 2019/11/22.
* 关键帧
*/
public class MyFloatKeyFrame {
public MyFloatKeyFrame(float mFraction, float mValue) {
this.mFraction = mFraction;
this.mValue = mValue;
}
float mFraction;//当前动画的百分比
Class mValueType;//float 返回什么类型的值 默认是float
float mValue;//具体值
public float getmFraction() {
return mFraction;
}
public void setmFraction(float mFraction) {
this.mFraction = mFraction;
}
public float getmValue() {
return mValue;
}
public void setmValue(float mValue) {
this.mValue = mValue;
}
}
这个里面的MyFloatKeyFrame,存储了很多关键帧,这个还是比较难理解的
package com.asyn.task.library;
import android.animation.FloatEvaluator;
import android.animation.TypeEvaluator;
import java.util.Arrays;
import java.util.List;
/**
* Created by ThinkPad on 2019/11/22.
*关键帧管理类
*/
public class MyKeyFrameSet {
//存储关键帧
List mykeyFrames;
//类型估值其
TypeEvaluator mEvaluator;
public MyKeyFrameSet(MyFloatKeyFrame... keyFrame){
mykeyFrames = Arrays.asList(keyFrame);
mEvaluator =new FloatEvaluator();
}
public static MyKeyFrameSet ofFloat(float[] values) {
if (values.length <= 0){
return null;
}
int numKeyframes = values.length;
//关键帧初始化 for
MyFloatKeyFrame keyFrame[] =new MyFloatKeyFrame[numKeyframes];
keyFrame[0] = new MyFloatKeyFrame(0,values[0]);
for (int i=0;i
梳理一下,我们可以发现,所有初始化和关联完成以后,真正起作用调度的是VSNCManager,它一直在while(true)循环,
1,每隔16秒就调用一下MyObjectAnimator#doAnimationFrame方法将传过来的3000毫秒平均分成16份,
2,通过调用MyPropertyValuesHodler#setAnimatedValue方法,将传过来的fraction值,
3,再去调MyKeyFrameSet#getValues方法,去计算真正移动的距离的数值,将数值返回给MyPropertyValuesHodler#setAnimatedValue
4,MyPropertyValuesHodler#setAnimatedValue通过反射将具体的移动数值设置给View
这个是别人的总结: