Android 动画 原理解析
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.实现原理
-
Animation对象保存了需要变化的参数,比如fromValue,toValue等等
-
Transformation对象维护一个matrix和alpha值,来记录不同动画的变化结果
-
在ViewGroup中,为每个child调用draw()方法时,会在canvas上应用当前动画效果的transformation,然后进行绘制
-
startAnimation后,会调用view的invalidate方法进行重绘,每次绘制完后,如果动画并没有完成,还会继续调用invalidate直至动画完成
-
由(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)