前言:

我们选择了android开发或ios开发,是不是见到不错的app,就想看看他们的一些动画效果,交互体验,以及一些有创意的功能。今天我们就来一起学习讲解android中的动画,以及动画原理,以便后续独立开发一些复杂的动画。

动画分类:

那android为我们提供了哪些动画呢:目前是三种:Tween动画,Frame动画,以及3.0后推出的Property动画即属性动画。

Tween动画:通过对场景的对象进行图形变化(包括缩放,平移,旋转,改变透明度)来产生动画效果。

Frame动画:顺序的播放事先做好的图像。类似放电影。

Property动画:即通过改变对象的实际属性来达到动画效果。

(这里为啥说实际属性呢,因为上述两种动画虽然对象在视觉上的确是移动了,但通过真实测试,并没有改变属性,这也是经常遇到的一个面试题目)

动画的代码分析:

我们在写布局定义控件一个道理,既可以layout定义,也可以代码定义,没疑问吧,Tween动画与Frame动画也可以用XML和代码两种方式来完成,殊途同归都是为了构建Animation对象:(说到这,其实大家学习编程,也要类比)

Animation trans = new TranslateAnimation(...);

这时候最重要的一个对象Animation就出现了,我们先看看Animation对象:

/*** Abstraction for an Animation that can be applied to Views, Surfaces, or* other objects. See the {@link android.view.animation animation package* description file}.*/
public abstract class Animation implements Cloneable 

从源码可以看粗, Animation对象是一个抽象类。TranslateAnimation,ScaleAnimation等都继承自Animation。那我们在看看具体类 TranslateAnimation的代码

    @Overrideprotected 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);}@Overridepublic void initialize(int width, int height, int parentWidth, int parentHeight) {super.initialize(width, height, parentWidth, parentHeight);mFromXDelta = resolveSize(mFromXType, mFromXValue, width, parentWidth);mToXDelta = resolveSize(mToXType, mToXValue, width, parentWidth);mFromYDelta = resolveSize(mFromYType, mFromYValue, height, parentHeight);mToYDelta = resolveSize(mToYType, mToYValue, height, parentHeight);}

我只拿了一部分代码,自己可以具体看看,该类除了构造函数外,有两个比较重要的方法: applyTransformation 以及 initialize方法。

这两个方法都是重写的方法。我们继续寻根求源,看看子类里的方法在什么时候调用的。经过搜索,applyTransformation实在Animation类的getTransfromation类中调用。

public boolean getTransformation(long currentTime, Transformation outTransformation) {if (mStartTime == -1) {mStartTime = currentTime;}final long startOffset = getStartOffset();final long duration = mDuration;float normalizedTime;if (duration != 0) {normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) /(float) duration;} else {// time is a step-change with a zero durationnormalizedTime = currentTime < mStartTime ? 0.0f : 1.0f;}final boolean expired = normalizedTime >= 1.0f;mMore = !expired;if (!mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) {if (!mStarted) {fireAnimationStart();mStarted = true;if (USE_CLOSEGUARD) {guard.open("cancel or detach or getTransformation");}}if (mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);if (mCycleFlip) {normalizedTime = 1.0f - normalizedTime;}final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);applyTransformation(interpolatedTime, outTransformation);}

而getTransformation方法实际是由系统调用的,根据动画当前时间计算Transformation信息,而实现类里的applyTransformation则是根据getTransformation里计算出的信息进行实际的动画实现。那我们一直说的Transformation是啥呢(Animation类的两个比较重要的属性就是监听和这个Transformation,其中AnimationListener是监听动画的开始,结束等),我们继续回到animation源码里去查找,然后看看Transformation到底都有啥:

public class Transformation {/*** Indicates a transformation that has no effect (alpha = 1 and identity matrix.)*/public static final int TYPE_IDENTITY = 0x0;/*** Indicates a transformation that applies an alpha only (uses an identity matrix.)*/public static final int TYPE_ALPHA = 0x1;/*** Indicates a transformation that applies a matrix only (alpha = 1.)*/public static final int TYPE_MATRIX = 0x2;/*** Indicates a transformation that applies an alpha and a matrix.*/public static final int TYPE_BOTH = TYPE_ALPHA | TYPE_MATRIX;protected Matrix mMatrix;protected float mAlpha;protected int mTransformationType;private boolean mHasClipRect;private Rect mClipRect = new Rect();

这里包含了Matrix,mAlpha等,mAlpha是存放透明度度信息的,而Matrix,则存放了平移,旋转等信息,这二者构成了Transformation信息的载体。
是否注意到了上边applyTransformation里有个matrix的调用 t.getMatrix()。

这个Matrix到底是什么,但从字母看是矩阵,我们线性代数,数据结构都经常接触到矩阵,动画里的矩阵干嘛用的,我们看回Matrix类

public class Matrix {public static final int MSCALE_X = 0;   //!< use with getValues/setValuespublic static final int MSKEW_X  = 1;   //!< use with getValues/setValuespublic static final int MTRANS_X = 2;   //!< use with getValues/setValuespublic static final int MSKEW_Y  = 3;   //!< use with getValues/setValuespublic static final int MSCALE_Y = 4;   //!< use with getValues/setValuespublic static final int MTRANS_Y = 5;   //!< use with getValues/setValuespublic static final int MPERSP_0 = 6;   //!< use with getValues/setValuespublic static final int MPERSP_1 = 7;   //!< use with getValues/setValuespublic static final int MPERSP_2 = 8;   //!< use with getValues/setValues/** @hide */public final static Matrix IDENTITY_MATRIX = new Matrix() {void oops() {throw new IllegalStateException("Matrix can not be modified");}@Overridepublic void set(Matrix src) {oops();}@Overridepublic void reset() {oops();}@Overridepublic void setTranslate(float dx, float dy) {oops();}@Overridepublic void setScale(float sx, float sy, float px, float py) {oops();}@Overridepublic void setScale(float sx, float sy) {oops();}@Overridepublic void setRotate(float degrees, float px, float py) {oops();}@Overridepublic void setRotate(float degrees) {oops();}@Overridepublic void setSinCos(float sinValue, float cosValue, float px, float py) {oops();}@Overridepublic void setSinCos(float sinValue, float cosValue) {oops();}@Overridepublic void setSkew(float kx, float ky, float px, float py) {oops();}@Overridepublic void setSkew(float kx, float ky) {oops();}
 @Overridepublic boolean setConcat(Matrix a, Matrix b) {oops();return false;}@Overridepublic boolean preTranslate(float dx, float dy) {oops();return false;}@Overridepublic boolean preScale(float sx, float sy, float px, float py) {oops();return false;}@Overridepublic boolean preScale(float sx, float sy) {oops();return false;}@Overridepublic boolean preRotate(float degrees, float px, float py) {oops();return false;}@Overridepublic boolean preRotate(float degrees) {oops();return false;}@Overridepublic boolean preSkew(float kx, float ky, float px, float py) {oops();return false;}@Overridepublic boolean preSkew(float kx, float ky) {oops();return false;}@Overridepublic boolean preConcat(Matrix other) {oops();return false;}@Overridepublic boolean postTranslate(float dx, float dy) {oops();return false;}@Overridepublic boolean postScale(float sx, float sy, float px, float py) {oops();return false;}@Overridepublic boolean postScale(float sx, float sy) {oops();return false;}@Overridepublic boolean postRotate(float degrees, float px, float py) {oops();return false;}@Overridepublic boolean postRotate(float degrees) {oops();return false;}

我们看到上述源码,setXXX,PreXXX,postXXX,是对translate,scale,rotate的设置。也就是getMatrix()是获取得到当前帧的动画中在矩阵中的信息。

其实android 中的 Matrix是个3*3的矩阵,包含的是平移,旋转,缩放的区域,进而实现对平移,旋转,缩放的动画实现。

Matrix的具体使用,放在后续讲解,今天只从源代码分析。如果同学急于学习,请先看一篇讲解吧:

Matrix

动画的使用:

我们在执行动画时,经常是view.startAnimation.看下源码:

    /*** Start the specified animation now.** @param animation the animation to start now*/public void startAnimation(Animation animation) {animation.setStartTime(Animation.START_ON_FIRST_FRAME);setAnimation(animation);invalidateParentCaches();invalidate(true);}

这个是view的启动执行动画的方法,setAnimation设置动画对象,并赋给成员变量mCurrentAnimation,然后invalidate() 重绘自身。

重绘视图的时候调用到drawChild,这时候获取与View绑定的Animation,在设置的动画时间不结束,就会变换矩阵Matrix,绘制完成,在获取新的一帧的换换矩阵,知道动画结束。在绘制子视图时drawChild(),会调用方法:

 /*** This method is called by ViewGroup.drawChild() to have each child view draw itself.** This is where the View specializes rendering behavior based on layer type,* and hardware acceleration.*/boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {final boolean hardwareAcceleratedCanvas = canvas.isHardwareAccelerated();/* If an attached view draws to a HW canvas, it may use its RenderNode + DisplayList.** If a view is dettached, its DisplayList shouldn't exist. If the canvas isn't* HW accelerated, it can't handle drawing RenderNodes.*/boolean drawingWithRenderNode = mAttachInfo != null&& mAttachInfo.mHardwareAccelerated&& hardwareAcceleratedCanvas;boolean more = false;final boolean childHasIdentityMatrix = hasIdentityMatrix();final int parentFlags = parent.mGroupFlags;if ((parentFlags & ViewGroup.FLAG_CLEAR_TRANSFORMATION) != 0) {parent.getChildTransformation().clear();parent.mGroupFlags &= ~ViewGroup.FLAG_CLEAR_TRANSFORMATION;}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);concatMatrix = a.willChangeTransformationMatrix();if (concatMatrix) {mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;}transformToApply = parent.getChildTransformation();} else {if ((mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_TRANSFORM) != 0) {// No longer animating: clear out old animation matrixmRenderNode.setAnimationMatrix(null);mPrivateFlags3 &= ~PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;}if (!drawingWithRenderNode

此方法里有调用了View.applyLegacyAnimation()方法,我们继续,可以看到源码里调用了Animation类的getTransformation方法,进而一步一步又回到上述分析的applyTransformation方法。这样,这个调用过程就关联起来了。

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());a.initializeInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop);if (mAttachInfo != null) a.setListenerHandler(mAttachInfo.mHandler);onAnimationStart();}final Transformation t = parent.getChildTransformation();boolean more = a.getTransformation(drawingTime, t, 1f);if (scalingRequired && mAttachInfo.mApplicationScale != 1f) {if (parent.mInvalidationTransformation == null) {parent.mInvalidationTransformation = new Transformation();}invalidationTransform = parent.mInvalidationTransformation;a.getTransformation(drawingTime, invalidationTransform, 1f);} else {invalidationTransform = t;

通过上述分析,Tween动画的流程也就走通了。大体总结时:

1.view.startAnimation 启动动画

2.invalidate() 重绘

3.执行drawChlild,进而调用draw方法获取animation对象

4.View.applyLegacyAnimation方法里调用Animation类的getTransformation方法

5.在具体类如Scaleanimation类实现Animation类的applyTransformation方法

6.在applyTransformation方法里通过矩阵(Matrix)变换实现动画

从而动画原理就是这个过程,对于矩阵的实现,后续会有文章展现。需要注意的是还有AnimationUtils(辅助类),AnimationSet(继承Animation),大家可以去分析分析。

最后需要注意的是:执行动画时并不是控件本身在改变,而是它的父view完成的,控件本身的位置并没有改变,这点需要与属性动画区分开。

Android 动画分析之Tween动画分析相关推荐

  1. android 同根动画_android 动画系列 (1) - tween 动画(view动画)

    这是我这个系列的目录,有兴趣的可以看下: android 动画系列 - 目录 tween 动画早些时候我们也叫补间动画(我也不知道为啥),现在也有叫 view 动画的.tween动画是2.X 时代的产 ...

  2. 【Android动画】之Tween动画 (渐变、缩放、位移、旋转)

    Android 平台提供了两类动画. 一类是Tween动画,就是对场景里的对象不断的进行图像变化来产生动画效果(旋转.平移.放缩和渐变). 第二类就是 Frame动画,即顺序的播放事先做好的图像,与g ...

  3. android 左移动画_Android研究院之游戏开发Tween动画的实现(十九)

    今天和大伙讨论一下Android开发中的Tween动画的实现.首先它和上一章我们讨论的Frame动画同属于系统提供的绘制动画的方法.Tween动画主要的功能是在绘制动画前设置动画绘制的轨迹,包括时间, ...

  4. Android动画之Property属性动画

    2019独角兽企业重金招聘Python工程师标准>>> 为什么引入属性动画? 大家都知道Android常见的动画有tween动画,frame动画.但是随着人们对动画的要求不断提高, ...

  5. android使用属性动画代替补间动画

    本文参考Android属性动画完全解析(上),初识属性动画的基本用法 android3.0之前一共有两种动画,分别是frame动画和tween动画,关于这两种动画如果不了解可以查看我之前的文章andr ...

  6. Android动画了解—一些项目案例分析

    文章目录 写在前面 带路径运动的过渡动画 整体缩放的效果 整体宽高改变的过渡效果 两个界面布局的过渡效果 带水波纹布局的效果 几行代码实现转圈圈的效果 界面翻转的效果 写在前面 最近听说MIUI 12 ...

  7. Android 9.0 开关机动画流程分析

    Android开机动画流程的启动主要是在Surfaseflinger里面完成的,具体代码如下: /frameworks/native/services/surfaceflinger/StartProp ...

  8. Android Activity跳转动画 - overridePendingTransition用法及原理分析

    overridePendingTransition()是在Activity类中实现的一个用来实现跳转动画的方式,也是最常使用的方法. overridePendingTransition() 先看这个名 ...

  9. Android动画之Tween动画实战

    Android动画分为Tween动画和Frame动画,上一节通过一个实例介绍了Frame动画,本节将介绍Tween动画.Tween可以把对象进行缩小.放大.旋转和渐变等操作. Tween动画有四个主要 ...

最新文章

  1. 68.iOS设备尺寸及型号代码(iPhoneXR/XS)
  2. java判断两个时间区间是否有重合
  3. java sapi.spvoice 更改发音人_我最喜欢的几个Java开发工具,推荐8个给你们!
  4. 怎样设置电脑壁纸_谷歌地球实时壁纸,电脑和手机实现方案都在这里了
  5. 自动驾驶7-4 自动驾驶汽车简介全面总结 Congratulations on Completing Course 1
  6. php 解析今日头条视频,今日头条的视频地址解析方法
  7. 订单系统设计,消息队列幂等处理思路
  8. python罗马数字转换,Python 罗马数字转换整数
  9. JN5169 Bootload 烧录过程和DIY烧录程序(一)
  10. 计算机硬盘 半导体存储器 相关概念
  11. 【org.mybatis.spring.MyBatisSystemException】There is no getter for property named ‘array‘‘
  12. OC:跟随小码哥一起学习KVC
  13. HTML,CSS,font-family:中文字体的英文名称 (比如:宋体 微软雅黑)的列表
  14. 怎样远程控制别人的电脑
  15. 【JAVA程序设计】(C00046)javaweb图书借阅管理系统
  16. mPEG-SS 甲氧基PEG琥珀酰亚胺丁二酸酯
  17. 含论文+辩论PPT+源码等]微信小程序ssm社区心理健康服务平台+后台管理系统
  18. C++Primer第五版 习题答案 目录
  19. 杰理之修改提高摄像头源视频输出帧率,确定摄像头源输出高帧率【篇】
  20. outlook 如何预订会议和会议室

热门文章

  1. ITIL(Information Technology Infrastructure Library )
  2. 离职是件愉快的事情吗?附离职当月工资算法说明
  3. 默认让网页打开出现360极速模式
  4. go语言学习—启动go
  5. Few-shot Learning for Trajectory-based Mobile Game Cheating Detection
  6. sqlserver2008 调试存储过程
  7. 提供“以图搜图”完整解决方案,博云视觉让各行业都可以方便地“以图搜图”
  8. php 连接mysql 天龙八部
  9. Linux内核第一宏:container_of
  10. CoInitialize浅析