Android高级 -属性动画--源码层分析自定义动画框架

学习内容: 

定义属性动画:更改view的属性值,通过调用invatate()来刷新。

动画种类:

 如果让你设计一个属性动画,该如何设计?

 属性动画使用

动画的本质

所以需要对这些方法进行封装

为什么要将动画分解成不同的关键帧,

原因:动画是需要时间开销才能完成的。如果不给出关键帧动画,动画过程将无法控制。在不同的时间点

 空间的状态也不一样。

例如: 让一个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<View> 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<View>(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 

ProPertyVauwaHodler

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:通过反射方式进行属性赋值

VSYNCManager 类

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<AnimationFrameCallback> 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<MyFloatKeyFrame> 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<numKeyframes;i++){
            //计算
            keyFrame[i] = new MyFloatKeyFrame((float) i/(numKeyframes -1 ),values[i]);
        }

        return new MyKeyFrameSet(keyFrame);

    }



    public Object getValues(float fraction){
        MyFloatKeyFrame prevKeyframe = mykeyFrames.get(0);
        for (int i=0;i<mykeyFrames.size();i++){
            MyFloatKeyFrame nextKeyframe =mykeyFrames.get(i);
            if (fraction<nextKeyframe.getmFraction()){
            //关键部分  间隔百分比===两帧之间的百分比  就是1f  到  2f  的 之间走了多少百分比
                float intervalFraction = (fraction - prevKeyframe.getmFraction())/
                        (nextKeyframe.getmFraction() - prevKeyframe.getmFraction());

                return mEvaluator == null ?
                        prevKeyframe.getmValue() + intervalFraction *(nextKeyframe.getmValue()):
                mEvaluator.evaluate(intervalFraction,prevKeyframe.getmValue(),nextKeyframe.getmValue());
            }

            prevKeyframe = nextKeyframe;//切帧操作
        }

        //完成===返回最后一帧的具体的值
        return mykeyFrames.get(mykeyFrames.size() - 1 ).getmValue();
    }

}

 

梳理一下,我们可以发现,所有初始化和关联完成以后,真正起作用调度的是VSNCManager,它一直在while(true)循环,

1,每隔16秒就调用一下MyObjectAnimator#doAnimationFrame方法将传过来的3000毫秒平均分成16份,

2,通过调用MyPropertyValuesHodler#setAnimatedValue方法,将传过来的fraction值,

3,再去调MyKeyFrameSet#getValues方法,去计算真正移动的距离的数值,将数值返回给MyPropertyValuesHodler#setAnimatedValue

4,MyPropertyValuesHodler#setAnimatedValue通过反射将具体的移动数值设置给View

 

这个是别人的总结:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值