Android动画原理

Android动画原理

一、前言
Android动画包含三种:补间动画(Tween Animation),帧动画(Frame Animation),属性动画 (Property Animation)。其中属性动画是从Android 3.0之后加入的。
 
本文着重介绍三种动画的实现原理,阅读本文的前提是应该可以简单的使用上述三种动画。
如不熟悉可以参考以下链接,
 
二、补间动画原理(Tween Animation)
 
原理:在绘制的过程中,尝试获取动画在当前时刻的变换,然后应用到view的绘制中。
 
说明:
绘制是显示view所必不可少的过程,通过view的draw方法可以看到绘制的流程。
查看View.java的源码,可以发现有两个draw方法,一个有一个参数,一个有三个参数。分别是
 
public void draw(Canvas canvas)
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime)
 
其中一个参数的draw方法是具体的绘制过程(绘制背景、绘制内容、绘制前景...)
含有三个参数的draw方法是由父布局调用的(ViewGroup.drawChild),因为Android绘制的过程是从根布局开始,
子View是否能够绘制由父布局决定。这个方法就是提前处理绘制的位置的地方,补间动画也是在此处进行了处理以实现动画的效果。
 
先举一个例子,
 
Animation translateAnimation = new TranslateAnimation(0, 100, 0, 0);
translateAnimation.setDuration(500);
translateAnimation.setInterpolator(new AccelerateInterpolator());
translateAnimation.setFillAfter(true);//设置动画结束后保持当前的位置(即不返回到动画开始前的位置)
imageView.startAnimation(translateAnimation);
 


一个动画重要的东西就四个部分,开始时间,结束时间,做什么,如何做。
上述是一个imageView在x方向移动的动画,可以看到它符合上面所说的四个部分,
做什么:沿x方向移动100
如何做:直线且不断加速
开始时间:startAnimtion调用的那一刻
结束时间: 开始时间+500
 
从上面也大致了解到补间动画的框架是通过先建立一个Animation对象并对其设置一些属性,然后将它与一个view建立关联,
最后这个view可以执行这个动画。
 
由此我们开始分析Animation的源码(基于Android 6.0):
Animation及相关类的源码在frameworks/base/core/java/android/view/animation包中。
 
其中Animation类是一个抽象类,虽然它没有抽象方法,但是它有一个空方法
protected void applyTransformation(float interpolatedTime, Transformation t)
可以看到,它有两个参数,第一个参数是 interpolatedTime 它代表插值后的时间,第二个参数是Transformation类的实例
 
Transformation是一个实体类,它主要的内容是透明度和一个矩阵。
所以子类实现了applyTransformation方法后可以针对插值时间来对Transformation做一定的操作来实现变化。
 
同时可以看到 applyTransformation是在Animtaion的getTransformation中调用的,
public boolean getTransformation(long currentTime, Transformation outTransformation)
其中currentTime作为一个参数,虽然期望的是当前时间,但是不是用还是由调用者决定。
第二个参数目的是调用者传进来作为收集变化信息
 
此方法除了调用回调(开始,结束,重复),重点是调用applyTransformation前的这一句
final float interpolatedTime = mInterpolator. getInterpolation(normalizedTime);
 
插值器的目的是为了将控制动画速度的过程抽离出来,它是通过改变时间来改变最终效果。
 
normalizedTime 的范围是0.0f~1.0f
但是interpolatedTime可以超出这个范围
 
所以在实现applyTransformation的时候需要考虑插值后的时间这个范围之外的情况。
 
下面以TranslateAnimation和AccelerateInterpolator来说明是如何实现的
可以知道
TranslateAnimation主要实现 applyTransformation方法
AccelerateInterpolator主要实现 getInterpolation方法
 
下面是两个方法的源码:
 
  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的矩阵偏移
  }
 
  public float getInterpolation(float input) {
  if (mFactor == 1.0f) {
             return input * input; //输入0.5 返回0.25
  } else {
             return (float)Math.pow(input, mDoubleFactor);
  }
  }


 
如此一来,动画的变化,就隐藏在Transformation之中了。
既然已经知道了如何变化,现在就需要系统使用这种变化即应用到绘制中了。
 
移步到View.java之中的draw(--,--,--)方法,
 
其中applyLegacyAnimation方法是用来获取变换的,
其中有两个部分需要注意
 a.getTransformation(drawingTime, invalidationTransform, 1f);
 
a.getInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop, region,
  invalidationTransform);
 
第一个就是获取变换,第二个是获取绘制无效区域。
其实这个无效区域是绘制后的无效区域,因为很有可能需要下次绘制(保证动画连续性)。
 
而view在draw之中
  if (transformToApply != null) {
  if (concatMatrix) {
  if (drawingWithRenderNode) {
  renderNode.setAnimationMatrix(transformToApply.getMatrix());
  } else {
  // Undo the scroll translation, apply the transformation matrix,
  // then redo the scroll translate to get the correct result.
  canvas.translate(-transX, -transY);
  canvas.concat(transformToApply.getMatrix());
  canvas.translate(transX, transY);
  }
  parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
  }
 
  float transformAlpha = transformToApply.getAlpha();
  if (transformAlpha < 1) {
  alpha *= transformAlpha;
  parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
  }
  } 


分别从硬件加速和软件绘制上 对canvas进行矩阵变换.
除了TranslateAnimation,系统还有AlphaAnimation和ClipRectAnimation可供选择.
 
 
三、逐帧动画原理(Frame Animation)
 
原理:使用了Choreographer机制
 
AnimationDrawable类是一个实现了逐帧动画的类,可以看出,它只用来进行图片的动态切换.
AnimationDrawable类源码在frameworks/base/graphics/java/android/graphics/drawable/中
 
public class AnimationDrawable extends DrawableContainer implements Runnable, Animatable
首先看到AnimationDrawable继承了DrawableContainer,因为DrawableContainer是一个drawable的容器,可以保存多个图片
同时,实现了Runnable接口,重写了run方法
 
根据源码中的start方法,它调用了setFrame方法,方法内部最重要的调用就是调用了
 scheduleSelf(this, SystemClock.uptimeMillis() + mAnimationState.mDurations[frame]);
上述方法实现是在Drawable类实现的
  public void scheduleSelf(Runnable what, long when) {
  final Callback callback = getCallback();
  if (callback != null) {
  callback.scheduleDrawable(this, what, when);
  }
  }


其中callback一般是drawable相关联的view.
可以看出,它接着回调了view的scheduleDrawable方法
而这个方法最终会
 mAttachInfo.mViewRootImpl.mChoreographer.postCallbackDelayed(
  Choreographer.CALLBACK_ANIMATION, what, who,
  Choreographer.subtractFrameDelay(delay));


来实现delay后,可以绘制下一帧的效果.
因为AnimationDrawable所实现了runnable接口的run方法就是执行nextFrame.
 
同时,AnimatedStateListDrawable和AnimatedVectorDrawable和AnimatedRotateDrawable(隐藏)
都是具备一定的动画效果
 
其中,AnimatedStateListDrawable是在view状态切换时可以实现两个状态直接的渐变
如果想了解更多,需要对drawable有所了解.
 
 
四、属性动画原理(Property Animation)
 
原理:使用了Choreographer机制
 
简单的说,Chreographer是组织上层进行处理绘制的控制类,它会在每次vsync信号来临时,执行与绘制相关的过程.
 
属性动画相关的方法在源码中所在的位置是frameworks/base/core/java/android/view/animation
 
属性动画的基类是Animator.
与Animation(补间动画)不同,Animator的确定过程发生在绘制之前(甚至是布局之前).
因为ValueAnimator引入了Choreographer,Choreographer是Vsync信号到来后进行view更新的控制类。
它通过mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimate, null);
这个动作来实现下次绘制之前,可以执行 mAnimate任务来设置view的一些属性来实现动画。
 
上述所说的view的一些属性包括X,translateX,TranslateZ,ScaleX......等属性
 
对这些属性的控制都会放到一个矩阵(Matrix),而这个矩阵放在RenderNode中
 
需要注意的是,虽然RenderNode主要为硬件渲染服务,但是它保存了一些属性是软件渲染也需要的,比如上面说的矩阵.
 
通过硬件加速绘制时,因为矩阵已经在RenderNode中了,所以在draw方法中不用做特殊处理.
 
而在软件渲染中(draw方法),
  if (!childHasIdentityMatrix && !drawingWithRenderNode) {
  canvas.translate(-transX, -transY);
  canvas.concat(getMatrix());
  canvas.translate(transX, transY);
  }


其中,childHasIdentityMatrix 代表是不是单位矩阵
drawingWithRenderNode 代表是不是开启了硬件加速
 
 
说明:
观察ValueAnimator的源码
 
发现方法
  private void scheduleAnimation() {
  if (!mAnimationScheduled) {
  mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimate, null);
  mAnimationScheduled = true;
  }
  }


以及mAnimate为
  // Called by the Choreographer.
  final Runnable mAnimate = new Runnable() {
  @Override
  public void run() {
  mAnimationScheduled = false;
  doAnimationFrame(mChoreographer.getFrameTime());
  }
  };


 
你如果对ValueAnimator添加了更新监听(addUpdateListener)
那么你可以在每次更新的回调(发生在上面的doAnimationFrame里[实际是animateValue])
比如进行view.setTranslateX(10),这种处理在随后的绘制中就会有所体现
 
 
 
参考文档:
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值