Android 动画 原理解析

Android 动画 原理解析

  • 一.View动画
    • (一)基本使用
      • 1.代码实现
      • 2.xml实现
    • (二)原理解析
      • 1.实现原理
      • 2.源码分析
        • (1)Animation类
        • (2)流程分析
  • 二.布局动画
    • (一)基本使用
      • 1.xml实现
      • 2.代码实现
    • (二)原理解析
      • 1.实现原理
      • 2.源码分析
        • (1)解析设置LayoutAnimationController
        • (2)dispatchDraw()
  • 三.属性动画
    • (一)基本使用
      • 1.代码实现
      • 2.xml实现
    • (二)原理解析
      • 1.实现原理
      • 2.源码分析
        • (1)创建
        • (2)时间变化
        • (3)值更新
  • 四.动画对比

android提供两种动画方式,一种是最原始的View的动画,另一种是后来提供的属性动画

二者都可以实现大部分动画效果,也可以通过组合实现各种动画效果,并且支持xml和java两种生成方式

二者的实现原理和效果差异性还是比较大的,下面来看看这两种动画的使用和实现原理

一.View动画

View动画主要使用的是Animation这个类,其子类TranslateAniamtion,AlphaAnimation等实现不同的动画效果,比如平移,透明度,缩放,旋转等

AnimationSet类可以管理多个Animation,做组合动画

(一)基本使用

1.代码实现

//旋转动画(以自身的中心为原点,从0度转到360度)
RotateAnimation rotateAnimation = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
//透明度动画(从完全不可见到完全可见)
AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);
//平移动画(x轴方向,从自身原点平移500像素)
TranslateAnimation translateAnimation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.ABSOLUTE, 500, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0);
//AnimationSet集合控制多个动画(无法控制顺序,而且执行效果和add顺序还有关系...)
AnimationSet animationSet = new AnimationSet(true);
//统一设置所有Animation的duration为1000
animationSet.setDuration(1000);
//动画结束后保持位置(并不影响实际measure和layout)
animationSet.setFillAfter(true);
//顺序添加Animation
animationSet.addAnimation(rotateAnimation);
animationSet.addAnimation(alphaAnimation);
animationSet.addAnimation(translateAnimation);
//view开启动画
view.startAnimation(animationSet);

由代码可知,我们可以创建不同的Animation对象,设置不同的参数:fromValue,toValue,type(绝对值,相对于自身,相对于parent等)

当有多个动画效果时,可以创建多个Animation对象,然后使用一个AnimationSet对象,add这些Animation,也可以设置一些公共参数

最后,通过view.startAnimation(Animation)执行view的动画

2.xml实现

//在res/anim文件夹下定义
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000">
    <translate
        android:fromXDelta="0"
        android:toXDelta="500" />
    <alpha
        android:fromAlpha="0"
        android:toAlpha="1" />
set>
//代码调用
Animation animation = AnimationUtils.loadAnimation(this, R.anim.translate_in);
view.startAnimation(animation);

我们也可以通过声明xml文件来定义动画,就对应的AnimationSet对象,就对应的TranslateAnimation对象,其他的等都类似

在代码里,我们可以使用AnimationUtils提供的方法将xml解析成Animation对象再使用

(二)原理解析

1.实现原理

  1. Animation对象保存了需要变化的参数,比如fromValue,toValue等等

  2. Transformation对象维护一个matrix和alpha值,来记录不同动画的变化结果

  3. 在ViewGroup中,为每个child调用draw()方法时,会在canvas上应用当前动画效果的transformation,然后进行绘制

  4. startAnimation后,会调用view的invalidate方法进行重绘,每次绘制完后,如果动画并没有完成,还会继续调用invalidate直至动画完成

  5. 由(3)知,动画只是改变了绘制时的状态,而不是改变了view的measure和layout的位置和大小等属性

2.源码分析

(1)Animation类

Android 动画 原理解析_第1张图片
i.Animation类用于存储和应用其动画值,当绘制时,会调用到Animation对象的getTransformation方法,并传递一个Transformation对象,该方法将动画当前的参数应用到Transformation对象上

//默认的实现
public boolean getTransformation(long currentTime, Transformation outTransformation) {
	...
    //根据当前时间计算动画行进比例
    if (duration != 0) {
        normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) /
                (float) duration;
    } else {
        // time is a step-change with a zero duration
        normalizedTime = currentTime < mStartTime ? 0.0f : 1.0f;
    }

	//动画是否结束
    final boolean expired = normalizedTime >= 1.0f || isCanceled();
    mMore = !expired;

    if (!mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);

    if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) {
        ...
		//应用动画效果到Transformation对象上
        applyTransformation(interpolatedTime, outTransformation);
    }

    ...
	//返回值代表动画是否已经完成
    return mMore;
}
 
//AnimationSet的实现
public boolean getTransformation(long currentTime, Transformation t) {
    ...

	//遍历children,应用每一个动画效果
    for (int i = count - 1; i >= 0; --i) {
        final Animation a = animations.get(i);

        temp.clear();
        more = a.getTransformation(currentTime, temp, getScaleFactor()) || more;
        t.compose(temp);

        started = started || a.hasStarted();
        ended = a.hasEnded() && ended;
    }

    ...

	//是否所有动画均已完成
    return more;
}

一般动画就是调用自己的applyTransformation方法,应用动画参数;AnimationSet类要调用每个animation的applyTransformation方法,应用所有的动画效果

ii.Animation类应用动画效果调用applyTransformation方法

//TranslateAnimation
protected void applyTransformation(float interpolatedTime, Transformation t) {
    float dx = mFromXDelta;
    float dy = mFromYDelta;
    if (mFromXDelta != mToXDelta) {
        dx = mFromXDelta + ((mToXDelta - mFromXDelta) * interpolatedTime);//根据比例计算当前偏移值
    }
    if (mFromYDelta != mToYDelta) {
        dy = mFromYDelta + ((mToYDelta - mFromYDelta) * interpolatedTime);
    }
    t.getMatrix().setTranslate(dx, dy);//直接将偏移应用到Transformation的matrix上
}
 
//AlphaAnimation
protected void applyTransformation(float interpolatedTime, Transformation t) {
    final float alpha = mFromAlpha;
    t.setAlpha(alpha + ((mToAlpha - alpha) * interpolatedTime));//计算当前alpha值,应用到Transformation中
}

由代码可以看到,Animation类就是将自己的当前动画值,应用到Transformation的matrix和alpha上,用于绘制时改变绘制状态

(2)流程分析

i.startAnimation()

public void startAnimation(Animation animation) {
    animation.setStartTime(Animation.START_ON_FIRST_FRAME);
    setAnimation(animation);//保存Animation对象
    invalidateParentCaches();
    invalidate(true);//重绘
}

开启动画,其实实质就是记录Animation对象,调用invalidate进行重绘,重绘时应用Animation

ii.draw()

//ViewGroup为每个child调用draw方法是根据Animation来设置动画效果
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
    ...

    Transformation transformToApply = null;
    boolean concatMatrix = false;
    final boolean scalingRequired = mAttachInfo != null && mAttachInfo.mScalingRequired;
    final Animation a = getAnimation();
    if (a != null) {//有动画
        more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);//应用动画
        ...
    }

    ...

    float alpha = drawingWithRenderNode ? 1 : (getAlpha() * getTransitionAlpha());
    if (transformToApply != null
            || alpha < 1
            || !hasIdentityMatrix()
            || (mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_ALPHA) != 0) {
        if (transformToApply != null || !childHasIdentityMatrix) {
            ...

            if (transformToApply != null) {
                canvas.concat(transformToApply.getMatrix());//将Transformation的matrix应用到canvas上(concat拼接)
            }

            ...
        }

        ...
    }

    ...

    return more;
}
 
//获取变换后的Transformation对象
private boolean applyLegacyAnimation(ViewGroup parent, long drawingTime,
        Animation a, boolean scalingRequired) {
    Transformation invalidationTransform;
    final int flags = parent.mGroupFlags;
    final boolean initialized = a.isInitialized();
    if (!initialized) {//没有初始化
        a.initialize(mRight - mLeft, mBottom - mTop, parent.getWidth(), parent.getHeight());//使用child的size和parent的size初始化Animation(因为可以有相对自己、parent等类型)
        a.initializeInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop);//初始化绘制region,初始化时child的区域
        if (mAttachInfo != null) a.setListenerHandler(mAttachInfo.mHandler);
        onAnimationStart();//listener
    }

    final Transformation t = parent.getChildTransformation();
    boolean more = a.getTransformation(drawingTime, t, 1f);//应用Animation的效果到Transformation上
    if (scalingRequired && mAttachInfo.mApplicationScale != 1f) {
        ...
    } else {
        invalidationTransform = t;
    }

    if (more) {//如果动画没有执行完
        if (!a.willChangeBounds()) {//动画效果不会导致绘制区域大于原区域时,继续invalidate即可
			...
            parent.invalidate(mLeft, mTop, mRight, mBottom);
        } else {//否则要计算新的region,进行invalidate
            ...
            final RectF region = parent.mInvalidateRegion;
            a.getInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop, region,
                    invalidationTransform);//根据Transformation获取新的region
            ...
            final int left = mLeft + (int) region.left;
            final int top = mTop + (int) region.top;
            parent.invalidate(left, top, left + (int) (region.width() + .5f),
                    top + (int) (region.height() + .5f));//invalidate新的region
        }
    }
    return more;
}

由代码可知,view动画,就是在ViewGroup调用draw方法绘制child时,将child的Animation的参数,应用到Transformation上,然后将Transformation的效果应用到canvas上进行绘制

当动画没有完毕时,通过不断调用invalidate重绘来重复这一过程

view动画实质是在draw方法里完成,并不会影响view的measure和layout的实际大小和位置

二.布局动画

Android还提供了一直布局动画,其是用在ViewGroup上的,可以给这个ViewGroup的每个child指定统一的动画,实现一定的效果,最常见的比如在ListView的items的出现效果

(一)基本使用

1.xml实现

//在res/anim文件夹下定义
//指定每个child的动画在上一个child动画执行了多长时间后开始执行
 
//在ViewGroup中声明
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layoutAnimation="@anim/layout_translate_in"
    android:orientation="vertical"/>

2.代码实现

//从xml加载出LayoutAnimationController对象
LayoutAnimationController controller = AnimationUtils.loadLayoutAnimation(this, R.anim.layout_translate_in);
//设置到ViewGroup中
container.setLayoutAnimation(controller);
//开启LayoutAnimation
container.startLayoutAnimation();

(二)原理解析

1.实现原理

  1. ViewGroup保存LayoutAnimationController对象,无论是从属性中解析出来的还是通过setter设置的

  2. 在ViewGroup的dispatchDraw方法绘制children时,为每个child设置controller里指定的Animation对象进行执行,controller里控制执行顺序、delay等流程

  3. 由(2)可知,布局动画实质也是在绘制时,为每个child应用了统一的view动画而已

  4. ViewGroup只在第一次绘制时会启动布局动画,之后会取消掉相应flag,如果想要多次执行,可以手动调用startLayoutAnimation()

2.源码分析

(1)解析设置LayoutAnimationController

 //ViewGroup解析
case R.styleable.ViewGroup_layoutAnimation:
    int id = a.getResourceId(attr, -1);
    if (id > 0) {
        setLayoutAnimation(AnimationUtils.loadLayoutAnimation(mContext, id));//解析,设置
    }
 
//代码设置
public void setLayoutAnimation(LayoutAnimationController controller) {
    mLayoutAnimationController = controller;
    if (mLayoutAnimationController != null) {
        mGroupFlags |= FLAG_RUN_ANIMATION;//加入标识位
    }
}
 
//手动执行动画
public void startLayoutAnimation() {
    if (mLayoutAnimationController != null) {
        mGroupFlags |= FLAG_RUN_ANIMATION;//加入标识位
        requestLayout();//重新布局绘制
    }
}

通过AnimationUtils.loadLayoutAnimation()解析xml生成LayoutAnimationController对象

通过setLayoutAnimation设置到ViewGroup中,并加入FLAG_RUN_ANIMATION标识位,后续绘制时使用

手动启动动画时,requestLayout导致重新布局绘制

(2)dispatchDraw()

//dispatchDraw()
if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {//需要有FLAG_RUN_ANIMATION标识并且设置了controller对象才会进行动画
    final boolean buildCache = !isHardwareAccelerated();
    for (int i = 0; i < childrenCount; i++) {
        final View child = children[i];
        if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
            final LayoutParams params = child.getLayoutParams();
            attachLayoutAnimationParameters(child, params, i, childrenCount);将view的参数(index,count等)设置params里,用于计算每个view的动画参数
            bindLayoutAnimation(child);//将Animation绑定到每个view上
        }
    }
	...
    controller.start();//开启动画

    mGroupFlags &= ~FLAG_RUN_ANIMATION;//取消掉标识位,之后不会再启动动画(除非手动启动)
    mGroupFlags &= ~FLAG_ANIMATION_DONE;

    ...
}
 
private void bindLayoutAnimation(View child) {
    Animation a = mLayoutAnimationController.getAnimationForView(child);//根据params拿到布局动画里设置的Animation对象
    child.setAnimation(a);//设置给view
}

在ViewGroup调用dispatchDraw绘制每个child时,会根据是否有FLAG_RUN_ANIMATION和设置的controller对象来启动动画

启动动画时,其实就是把指定的Animation对象设置给每个child,其实本质就是给每个view设置view动画

在设置布局动画时会加入FLAG_RUN_ANIMATION标识,只在ViewGroup第一次绘制时开启动画,之后会取消掉标识,后续再想启动布局动画,可以手动startLayoutAnimation()

三.属性动画

上面说到的view动画是基于view的,且在绘制时改变view的展示效果

android提供的属性动画其实是一个变化过程,而与view无依赖关系,我们可以通过一个变化过程来完成自己想要的任何变化效果,包括了view,非常好使用

(一)基本使用

1.代码实现

//1.值动画
ValueAnimator animator = ValueAnimator.ofInt(0, 100);
animator.setInterpolator(new OvershootInterpolator());//加速器
animator.setRepeatCount(ValueAnimator.INFINITE);//重复次数
animator.setRepeatMode(ValueAnimator.REVERSE);//重复模式
animator.setStartDelay(1000);//开始延迟
animator.setDuration(1000);//duration
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {//更新监听
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        int value = (int) animation.getAnimatedValue();//拿到当前值使用
        ViewGroup.LayoutParams params = view.getLayoutParams();
        params.width = params.height = value;
        view.setLayoutParams(params);
    }
});
animator.start();//开启动画
 
//2.对象动画(属性)
//或用"translationX"代替,确保有setTranslationX(float)方法
ObjectAnimator animator = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, 0, 500);
animator.setAutoCancel(true);//API 18以上,当有其他处理该target的相应property的animator时,自动cancel掉这个
animator.start();
 
//3.组合动画
//(1).通过多个PropertyValuesHolder组合(同时执行)
PropertyValuesHolder tranXHolder = PropertyValuesHolder.ofFloat(View.TRANSLATION_X, 0, 500);
PropertyValuesHolder rotationXHolder = PropertyValuesHolder.ofFloat(View.ROTATION_X, 0, 360);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, tranXHolder, rotationXHolder);
animator.start();
 
//(2).通过AnimatorSet(控制顺序)
ObjectAnimator transAnim = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, 0, 500);
ObjectAnimator rotationAnim = ObjectAnimator.ofFloat(view, View.ROTATION, 0, 360);
ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0);

animatorSet = new AnimatorSet();
//同步执行
//animatorSet.playTogether(transAnim, rotationAnim, alphaAnim);
//顺序执行
//animatorSet.playSequentially(transAnim, rotationAnim, alphaAnim);
//流程控制(在alpha执行完成后,trans和rotation一起执行)
//animatorSet.play(transAnim).with(rotationAnim).after(alphaAnim);
//一个链式调用以play为基点展开,假如如果需要多个after或before,可以使用多个链式调用(同一个animator对象会当作一个)
animatorSet.play(transAnim).after(rotationAnim);
animatorSet.play(rotationAnim).after(alphaAnim);
//AnimatorSet的duration会覆盖每个Animator的duration
animatorSet.setDuration(1000);
animatorSet.start();
 
//4.View提供的简易动画(实质使用的也是属性动画ValueAnimator)
//x轴平移到500、旋转到360度,同时执行
view.animate().translationX(500).rotation(360).setDuration(1000).start();
  1. ValueAnimator:一个值变化动画,模拟了值的变化过程,我们可以拿到任意时刻的变化值去完成我们自己的效果

  2. ObjectAnimator:对象属性变化动画,可以设置一个target和property,即把值的变化过程,通过property对象应用到target上,比如view的动画操作,就变的易如反掌

  3. AnimatorSet:管理多个Animator的管理类,可以控制多个动画的执行顺序等各种方面

  4. PropertyValuesHolder:是Animator类真正构建动画信息的类,所以我们可以向一个Animator类里添加多个PropertyValuesHolder对象,来实现多个动画同时执行的效果

  5. ViewPropertyAnimator:View类提供的一个管理View动画的管理类,内部会封装view的一些基本动画操作,如平移、缩放等,我们可以通过简易api创建简单的动画效果执行,执行时本质用的也是ValueAnimator类实现

2.xml实现

//在res/animator文件夹下定义

    
    <animator
        android:valueFrom="0"
        android:valueTo="100" />
set>
//代码调用
Animator animator = AnimatorInflater.loadAnimator(this, R.animator.alpha_animator);

通过工具类AnimatorInflater.loadAnimator方法可以解析生成Animator对象

(二)原理解析

1.实现原理

Android 动画 原理解析_第2张图片

  1. 通过创建(有静态工厂方法)Animator类来设置动画的各种参数,包括duration、repeat、interpolator等,还有值变化参数

  2. 所有的值变化参数会交由PropertyValuesHolder的静态工厂方法,构建出相应的PropertyValuesHolder对象,该类是真正用于管理、计算、更新值变化的类

  3. PropertyValuesHolder内部,会将传入的值变化参数,封装成一个个相应的KeyFrame对象,维护在一个相应的KeyFrameSet对象中,保存下来,获取当前值时使用

  4. KeyFrameSet在获取当前值参数时,会使用维护的KeyFrame列表,计算得到当前的值

  5. AnimationHandler类为线程单例类,其内部使用的是一个线程单例类Choreographer,用来绑定当前线程looper,发送消息,执行回调的,回调的callback就是AnimationFrameCallback类,Animator实现了该接口

  6. 在动画开始(start())时,会使用AnimationHandler发送小心,callback为Animator自己,每次回调时,根据当前时间比例fraction,计算当前值,然后继续发送消息,直到动画完成

2.源码分析

这里我们先以创建一个简单的ValueAnimator为例,逐步分析下它的创建、时间变化、值更新过程:

(1)创建

ValueAnimator animator = ValueAnimator.ofInt(0,100);
//静态工厂方法创建
public static ValueAnimator ofInt(int... values) {
    ValueAnimator anim = new ValueAnimator();
    anim.setIntValues(values);
    return anim;
}
 
//将这些值变化,生成Holder对象保存
public void setIntValues(int... values) {
    ...
	setValues(PropertyValuesHolder.ofInt("", values));
    mInitialized = false;//flag
}
 
//Holder中静态工厂方法生成相应的Holder对象
public static PropertyValuesHolder ofInt(String propertyName, int... values) {
    return new IntPropertyValuesHolder(propertyName, values);
}
//构建Holder时,将values构建成List对象,保存到KeyFrameSet对象中
public IntPropertyValuesHolder(String propertyName, int... values) {
    super(propertyName);//保存property相关信息
    setIntValues(values);//设置值变化参数
}
public void setIntValues(int... values) {
    mValueType = int.class;
    mKeyframes = KeyFrameSet.ofInt(values);//生成相应的KeyFrameSet对象
}
 
//静态工厂方法生成KeyFrameSet对象
public static KeyframeSet ofInt(int... values) {
    int numKeyframes = values.length;
    IntKeyframe keyframes[] = new IntKeyframe[Math.max(numKeyframes,2)];//将每个阶段的值变化保存为KeyFrame对象
    if (numKeyframes == 1) {
        keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f);
        keyframes[1] = (IntKeyframe) Keyframe.ofInt(1f, values[0]);
    } else {
        keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f, values[0]);//第一帧fraction为0
        for (int i = 1; i < numKeyframes; ++i) {
            keyframes[i] =
                    (IntKeyframe) Keyframe.ofInt((float) i / (numKeyframes - 1), values[i]);//每一帧fraction为index/(count-1),按比例分
        }
    }
    return new IntKeyframeSet(keyframes);//将这些值变化帧保存到KeyFrameSet中
}
 
//静态工厂方法生成KeyFrame对象
public static Keyframe ofInt(float fraction, int value) {
    return new IntKeyframe(fraction, value);
}
IntKeyframe(float fraction, int value) {
    mFraction = fraction;//保存fraction
    mValue = value;//保存value
	...
}

(2)时间变化

//start()启动动画
private void start(boolean playBackwards) {
    ...
	//发起回调,进行下一次动画变化
    AnimationHandler animationHandler = AnimationHandler.getInstance();
	//添加Animator为callback,进行下次动画变化
    animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale));

    if (mStartDelay == 0 || mSeekFraction >= 0) {
        startAnimation();//开启动画
        if (mSeekFraction == -1) {
            setCurrentPlayTime(0);//执行第一帧
        } else {
            setCurrentFraction(mSeekFraction);
        }
    }
}
 
//添加回调,发起消息
public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
    if (mAnimationCallbacks.size() == 0) {
        getProvider().postFrameCallback(mFrameCallback);//使用Choreographer,通过handler发起message,回调到FrameCallback
    }
    if (!mAnimationCallbacks.contains(callback)) {
        mAnimationCallbacks.add(callback);//添加callback
    }
	...
}
 
//FrameCallback处理
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
    @Override
    public void doFrame(long frameTimeNanos) {
        doAnimationFrame(getProvider().getFrameTime());//执行当前动画变化,进行更新
        if (mAnimationCallbacks.size() > 0) {
            getProvider().postFrameCallback(this);//继续发起message,回调到自己,直到所有动画执行完毕,mAnimationCallbacks就为空集合了,就不用发起message了
        }
    }
};

(3)值更新

值更新

//1.开始动画
 private void start(boolean playBackwards) {
    ...
	//加入回调,绘制每一帧
    AnimationHandler animationHandler = AnimationHandler.getInstance();
    animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale));
	//立即开始,绘制第一帧或指定帧
    if (mStartDelay == 0 || mSeekFraction >= 0) {
        startAnimation();//开始动画,初始化一些属性
        if (mSeekFraction == -1) {
            setCurrentPlayTime(0);//绘制第一帧,时间相当于0(刚开始)
        } else {
            setCurrentFraction(mSeekFraction);
        }
    }
}
 
public void setCurrentPlayTime(long playTime) {
    float fraction = mDuration > 0 ? (float) playTime / mDuration : 1;//根据duration计算指定时间的fraction
    setCurrentFraction(fraction);//调用该方法可以从任一时间点执行
}
 
public void setCurrentFraction(float fraction) {
    ...
	//校准fraction
    final float currentIterationFraction = getCurrentIterationFraction(fraction);
	//调用该方法进行统一的更新步骤
    animateValue(currentIterationFraction);
}
 
//2.callback回调doAnimationFrame()方法,绘制每一帧
public final void doAnimationFrame(long frameTime) {
    AnimationHandler handler = AnimationHandler.getInstance();
    if (mLastFrameTime == 0) {
        //首次绘制(有开始延迟)
        handler.addOneShotCommitCallback(this);
        if (mStartDelay > 0) {
            startAnimation();
        }
        ...
    }
    mLastFrameTime = frameTime;
	//处理暂停、重启等状态
    if (mPaused) {
        mPauseTime = frameTime;
        handler.removeCallback(this);
        return;
    } else if (mResumed) {
        mResumed = false;
        ...
        handler.addOneShotCommitCallback(this);
    }
    
	//根据当前时间,调用animateBasedOnTime()方法计算fraction
    final long currentTime = Math.max(frameTime, mStartTime);
    boolean finished = animateBasedOnTime(currentTime);

	//处理结束动画
    if (finished) {
        endAnimation();
    }
}
 
boolean animateBasedOnTime(long currentTime) {
    boolean done = false;
    if (mRunning) {
		//根据duration、repeat,计算当前时间的fraction
        final long scaledDuration = getScaledDuration();
        final float fraction = scaledDuration > 0 ?
                (float)(currentTime - mStartTime) / scaledDuration : 1f;
        final float lastFraction = mOverallFraction;
        final boolean newIteration = (int) fraction > (int) lastFraction;
        final boolean lastIterationFinished = (fraction >= mRepeatCount + 1) &&
                (mRepeatCount != INFINITE);
        ...
        mOverallFraction = clampFraction(fraction);
		//校准fraction,最终也调用到animateValue()方法统一处理fraction变化
        float currentIterationFraction = getCurrentIterationFraction(mOverallFraction);
        animateValue(currentIterationFraction);
    }
    return done;
}

由代码可知,无论是动画刚开始第一帧,还是callback回调绘制每一帧,都会根据当前时间和动画起始时间以及repeat、duration等信息,算出当前的时间比例fraction,然后统一调用animateValue(fraction)进行动画更新

在callback回调到doAnimationFrame()方法绘制每一帧时,会实时处理动画的各种状态,比如pause,resume,end等

开始动画时,会进行一些属性的初始化,即调用initAnimation方法,这个方法会初始化PropertyValuesHolder等重要属性,在具体更新动画前,我们要看一下,究竟需要初始化哪些属性:

initAnimation

//1.ValueAnimator
void initAnimation() {
    if (!mInitialized) {
        int numValues = mValues.length;
        for (int i = 0; i < numValues; ++i) {
            mValues[i].init();//调用所有PropertyValuesHolder的init方法初始化
        }
        mInitialized = true;
    }
}
 
//2.ObjectAnimator
void initAnimation() {
    if (!mInitialized) {
        final Object target = getTarget();
        if (target != null) {
            final int numValues = mValues.length;
            for (int i = 0; i < numValues; ++i) {
                mValues[i].setupSetterAndGetter(target);//调用所有PropertyValuesHolder的该方法,进行属性setter/getter方法的反射初始化
            }
        }
        super.initAnimation();//super
    }
}
 
//3.PropertyValuesHolder的init方法
void init() {
	//初始化TypeEvaluator
    if (mEvaluator == null) {
        mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
                (mValueType == Float.class) ? sFloatEvaluator :
                null;
    }
	//将Evaluator传递到KeyFrameSet中使用
    if (mEvaluator != null) {
        mKeyframes.setEvaluator(mEvaluator);
    }
}
 
//4.PropertyValuesHolder初始化setter/getter
//FloatPropertyValuesHolder.setupSetter
void setupSetter(Class targetClass) {
	//传递进来的就是Property对象
    if (mProperty != null) {
        return;
    }
    // Check new static hashmap for setter method
    synchronized (sJNISetterPropertyMap) {
		//get in cache
        ...
        if (!wasInMap) {
			//根据指定的propertyName生成反射setter名:setXxx
            String methodName = getMethodName("set", mPropertyName);
            try {
				//反射查找target类中的该方法Method对象
                mJniSetter = nGetFloatMethod(targetClass, methodName);
            }
			//cache
			...
        }
    }
    ...
}

由代码可知,ValueAnimator初始化主要是初始化PropertyValuesHolder,主要是初始化TypeEvaluator,用于计算值变化

ObjectAnimator除了会处理上述属性,还会处理property相关的内容,正如前面所说,ObjectAnimator可以指定Property对象或者propertyName,来更新target,当使用Property对象时不作处理,而当使用propertyName时,就需要根据这个name,反射将相应的Method找到,更新时用其来更新target

说完了初始化,下面就来看看,当拿到fraction后,如何应用到动画上的:

update

//1.ValueAnimator
void animateValue(float fraction) {
    fraction = mInterpolator.getInterpolation(fraction);//Interpolator变速器计算当前速率下的fraction
    mCurrentFraction = fraction;
    int numValues = mValues.length;
    for (int i = 0; i < numValues; ++i) {
        mValues[i].calculateValue(fraction);//使用fraction,更新每个PropertyValuesHolder的当前状态值
    }
	//listener->update
    if (mUpdateListeners != null) {
        int numListeners = mUpdateListeners.size();
        for (int i = 0; i < numListeners; ++i) {
            mUpdateListeners.get(i).onAnimationUpdate(this);
        }
    }
}
 
//2.ObjectAnimator
void animateValue(float fraction) {
    ...
    super.animateValue(fraction);//super
    int numValues = mValues.length;
    for (int i = 0; i < numValues; ++i) {
        mValues[i].setAnimatedValue(target);//更新完每个PropertyValuesHolder的状态值后,将每个状态应用到target上
    }
}
 
//3.应用到target上
//FloatPropertyValueHolder
void setAnimatedValue(Object target) {
	//直接使用指定的property对象进行应用
    if (mFloatProperty != null) {
        mFloatProperty.setValue(target, mFloatAnimatedValue);
        return;
    }
    ...
	//反射调用Method,应用到target上
    if (mSetter != null) {
        try {
            mTmpValueArray[0] = mFloatAnimatedValue;
            mSetter.invoke(target, mTmpValueArray);
        } catch (InvocationTargetException e) {
            Log.e("PropertyValuesHolder", e.toString());
        } catch (IllegalAccessException e) {
            Log.e("PropertyValuesHolder", e.toString());
        }
    }
}
 
//4.Property
//View.TRANSLATION_X
public static final Property<View, Float> TRANSLATION_X = new FloatProperty<View>("translationX") {
    @Override
    public void setValue(View object, float value) {
        object.setTranslationX(value);//直接调用view的setTranslationX方法进行移动
    }
	...
};

由代码可知,更新值时,Animator会首先用我们指定的Interpolation变速器,计算当前速率下fraction的真实值

然后使用当前fraction去更新所有PropertyValuesHolder,即所有动画过程的当前值(calculateValue)

最后通知Listener进行更新,比如ValueAnimator中,我们通过listener的update方法来拿到当前真实值,来进行处理

而对于ObjectAnimator来说,我们给其设置的Property对象,或者通过propertyName反射出来的setter方法,在计算完所有PropertyValuesHolder值后,就可以直接应用到target上了,不需要我们自己处理

下面就来看看,PropertyValuesHolder是如何根据fraction来更新状态值的:

calculateValue

//1.calculateValue
//FloatPropertyValuesHolder
void calculateValue(float fraction) {
	//直接使用的是Holder维护的KeyFrameSet获取最新值,并保存下来
    mFloatAnimatedValue = mFloatKeyframes.getFloatValue(fraction);
}
 
//2.KeyFrameSet.getValue
//FloatKetFrameSet
public float getFloatValue(float fraction) {
    //只有两帧的情况、fraction大于1的情况等
	//普通情况
    FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0);//前一帧
    for (int i = 1; i < mNumKeyframes; ++i) {
        FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(i);//后一帧
        if (fraction < nextKeyframe.getFraction()) {//找到当前fraction位于之间的两个帧
            final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
            float intervalFraction = (fraction - prevKeyframe.getFraction()) /
                (nextKeyframe.getFraction() - prevKeyframe.getFraction());//根据当前fraction,计算在两帧之间的fraction
            float prevValue = prevKeyframe.getFloatValue();
            float nextValue = nextKeyframe.getFloatValue();
            // Apply interpolator on the proportional duration.
            if (interpolator != null) {
                intervalFraction = interpolator.getInterpolation(intervalFraction);
            }
            return mEvaluator == null ?
                    prevValue + intervalFraction * (nextValue - prevValue) :
                    ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
                        floatValue();//用TypeEvaluator,计算preValue至nextValue之间,比例为fraction的值
        }
        prevKeyframe = nextKeyframe;
    }
    // shouldn't get here
    return ((Number)mKeyframes.get(mNumKeyframes - 1).getValue()).floatValue();
}

由代码可知,PropertyValuesHolder计算值,其实就是通过一开始创建的帧集合(KeyFrameSet)去计算当前值

KeyFrameSet使用其维护的List,首先通过当前fraction,找到所在区间的前后两个KeyFrame,然后计算fraction在其内部的intervalFraction

通过TypeEvaluator,得到preValue和nextValue之间,比例为intervalFraction的值,就是当前的状态值

至此,一次动画值的更新就完成了,每次callback回调改变一次,连起来的绘制效果,就是动画

四.动画对比

以上就是android提供的View动画和属性动画的使用与原理,简单做个对比:

  1. 使用上,两者提供的基本功能都差不多,也都可以通过java代码和xml两种形式定义和生成

  2. 在动画集合上来说,AnimationSet和AnimatorSet都可以管理多个动画,但是AnimatorSet支持顺序控制等方式,可以更加灵活的组合动画

  3. View动画是与View强行绑定的,只能用于View;而属性动画是一个动画过程的抽象,既可以方便的完成View动画的效果,也可以自己定义任何的动画效果

  4. View动画是基于draw过程的,即动画效果是在View的draw过程时候完成的,只改变了绘制区域,而并没有改变View的真实位置和大小以及相应区域,容易造成一定的混乱;而属性动画改变的是View真实的位置和大小

  5. View动画支持了布局动画,属性动画没有,可以通过自己的逻辑控制来实现

综上所述,其实属性动画几乎可以支持全部情况下的动画,可以替代View动画,也是现在用的最多的。

你可能感兴趣的:(原理解析,android相关)