Android 动画 原理解析

本文详细解析了Android中的View动画、布局动画和属性动画的使用与原理解析,包括基本使用、实现原理和源码分析。View动画通过Animation类实现,仅改变绘制状态,不改变实际属性。布局动画在 ViewGroup 上应用,统一设置 child 的动画。属性动画作为独立的动画过程,可以作用于任意对象,提供更强大的动画效果。属性动画比View动画更灵活,适用于更多场景。
摘要由CSDN通过智能技术生成


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类

在这里插入图片描述
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)
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值