700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > Android动画-Animation原理解析

Android动画-Animation原理解析

时间:2024-03-08 23:05:22

相关推荐

Android动画-Animation原理解析

Android动画-Animation原理解析

一、概述

在android中动画分为3类,帧动画、补间动画、属性动画

今天要说的就是“补间动画”,补间动画的基类是Animation,具体的实现都在TranslateAnimation、ScaleAnimation、RotateAnimation、AlphaAnimation中实现的

这个动画的原理是将控件View在时间上连续的绘制,就形成了动画,但是这个动画有个2个主要缺点

1、控件的本身没有移动或者旋转,位置信息没有改变,只是在绘制View的时候进行了矩阵Matrix变换

,所以当你要点击控件的时候,必须要点击原来位置的,

2、这个方式实现动画扩展性不高,只能针对View的处理,

google意识到了这点,于是在android3.0推出了属性动画,具体可以在我的对应文章有所分析

今天通过分析Animation来学习借鉴下谷歌的思路,也是不错的

二、分析

首先我们来分析下这个类,这个是一个补间动画的基类,这个类是个抽象类

这个类主要作用:实现了补间动画的基本逻辑,已经提供了子类要实现的接口,形成了基本框架

我们来看下重要的api

//设置动画时间animation.setDuration(2000);//设置重复模式,这里有两种RESTART:这个是从头开始,Reverse:这个是反向回到起点animation.setRepeatMode(Animation.REVERSE);//这个是动画重复次数,如果设置-1就是无线次数,可以是Animation.INFINITEanimation.setRepeatCount(2);//设置为true,动画结束时停留在动画的最后一帧animation.setFillAfter(true);//设置为false,动画结束时候停留在动画的第一帧animation.setFillBefore(true);//重置当前动画,主要是做了清空资源,初始化变量,当我们调用了cancel后,如果要重新开始//就要先调用下reset哦animation.reset();//动画取消,这个取消不是暂停的意思,补间动画是没有暂停功能的,animation没有提供//这个方法调用后动画会setFillAfter 或者setFillBefore 的设置回到起点还是终点帧animation.cancel();//开始动画animation.start();//设置一个handler,来处理里面的各种监听事件animation.setListenerHandler(Handler)//设置动画监听animation.setAnimationListener();//设置插值器,这个可以改变动画的运动节奏,animation.setInterpolator(interpolator);//设置动画开始的延迟时间,默认是0 意思 立即开始动画animation.setStartOffset(2000);//按照scale 对animation和startOffset 进行比例缩放或者扩大animation.scaleCurrentDuration(scale);

以上就是Animation 经常用的api了

/*** Helper for getTransformation. Subclasses should implement this to apply* their transforms given an interpolation value. Implementations of this* method should always replace the specified Transformation or document* they are doing otherwise.** @param interpolatedTime The value of the normalized time (0.0 to 1.0)* after it has been run through the interpolation function.* @param t The Transformation object to fill in with the current* transforms.*/protected void applyTransformation(float interpolatedTime, Transformation t) {}

这个是Animation中需要子类重写的方法,需要根据自己的需求实现对应逻辑的

那么我们就来看下TranslateAnimation如何实现的

主要下面这三个方法

//这个方法就是初始化起始点和重点,还有对应的类型,这个类型包含三种//public static final int ABSOLUTE = 0; 绝对的类型,传入的是像素点//public static final int RELATIVE_TO_SELF = 1; 这个是相对于自己的长度,toXValue=2,2倍的自己width长度//public static final int RELATIVE_TO_PARENT = 2; 这个是相对于父视图的尺寸对比//public TranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue,int fromYType, float fromYValue, int toYType, float toYValue) {mFromXValue = fromXValue;mToXValue = toXValue;mFromYValue = fromYValue;mToYValue = toYValue;mFromXType = fromXType;mToXType = toXType;mFromYType = fromYType;mToYType = toYType;}/*interpolatedTime:这个是经过插值器计算完了之后的返回值Transformation:这个是一个转换器,是Animation和View的绘制的桥梁,Transformation里面有个Matrix变换矩阵,Animation的子类对这个转换器操作后,View拿到后,取出matrix来进行实际的转换*/@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);}//将这个值设置到matrixt.getMatrix().setTranslate(dx, dy);}//将不同的type类型 转换成像素//比如类型是RELATIVE_TO_SELF,相对于自己,自己控件本身width=200 那么这个最终的值是:mFromXValue*自己控件本身width/*protected float resolveSize(int type, float value, int size, int parentSize) {switch (type) {case ABSOLUTE:return value;case RELATIVE_TO_SELF:return size * value;case RELATIVE_TO_PARENT:return parentSize * value;default:return value;}}*/@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 这个方法是子类需要实现的方法,那问题来了,这个方法在什么地方调用的呢,很显然这个应该是父类Animation里调用的,我们能可以找到是在父类中的getTransformation中调用的

getTransformation方法是Animation的核心方法实现了插值器的标准值计算(0-1)还有对重复执行的控制逻辑

这个方法子类一般不用重写

public boolean getTransformation(long currentTime, Transformation outTransformation) {if (mStartTime == -1) {mStartTime = currentTime;}final long startOffset = getStartOffset();final long duration = mDuration;//这个参数是插值器Interpolation的标准值时间范围是0-1,这个是个进度的值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 || isCanceled();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 (NoImagePreloadHolder.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,这个方法是子类需要实现的,applyTransformation(interpolatedTime, outTransformation);}//这里面的逻辑主要是实现重复执行的控制//只有一圈执行完了 才能进入这里判断if (expired) {if (mRepeatCount == mRepeated || isCanceled()) {if (!mEnded) {mEnded = true;guard.close();fireAnimationEnd();}} else {//如果mRepeatCount>0 那么mRepeated,到时候mRepeatCount == mRepeated的时候就停止动画了//如果mRepeatCount==-1 那么就无线循环,一直动画,因为mRepeatCount == mRepeated 永远//不可能为true,//后面就一直返回mMore=trueif (mRepeatCount > 0) {mRepeated++;}if (mRepeatMode == REVERSE) {mCycleFlip = !mCycleFlip;}mStartTime = -1;//这里设置为true,这最终会传入到View中的draw方法,作为返回值的一部分//如果为true 就是动画正在进行,没有完,还要继续画mMore = true;//回调AnimationLister接口fireAnimationRepeat();}}if (!mMore && mOneMoreTime) {mOneMoreTime = false;return true;}return mMore;}

Animation在View的执行逻辑

我们先前说过这个动画的绘制是在View的draw中执行的,那我们就来找一下,有这么段代码

boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {/*省去部分代码*/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();//判断这个Animationif (a != null) {more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);concatMatrix = a.willChangeTransformationMatrix();if (concatMatrix) {mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;}transformToApply = parent.getChildTransformation();}

继续看,我们在View#applyLegacyAnimation方法中找到,那我们来看下吧

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();/*这个就是调用前面说的getTransformation方法的调用地方如果返回true,就标识动画要继续执行*/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;}//如果true,就继续调用parent的invalidate方法 继续循环draw//上层的ViewGroup会执行dispathchDraw 最后调用的子View的draw中 往复执行if (more) {if (!a.willChangeBounds()) {if ((flags & (ViewGroup.FLAG_OPTIMIZE_INVALIDATE | ViewGroup.FLAG_ANIMATION_DONE)) ==ViewGroup.FLAG_OPTIMIZE_INVALIDATE) {parent.mGroupFlags |= ViewGroup.FLAG_INVALIDATE_REQUIRED;} else if ((flags & ViewGroup.FLAG_INVALIDATE_REQUIRED) == 0) {// The child need to draw an animation, potentially offscreen, so// make sure we do not cancel invalidate requestsparent.mPrivateFlags |= PFLAG_DRAW_ANIMATION;//继续执行动画parent.invalidate(mLeft, mTop, mRight, mBottom);}} else {if (parent.mInvalidateRegion == null) {parent.mInvalidateRegion = new RectF();}final RectF region = parent.mInvalidateRegion;a.getInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop, region,invalidationTransform);// The child need to draw an animation, potentially offscreen, so// make sure we do not cancel invalidate requestsparent.mPrivateFlags |= PFLAG_DRAW_ANIMATION;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));}}return more;}

三、总结

通过上面的分析,Animation核心还是通过view的重绘来实现动画效果,重绘的过程需要设置矩阵Matrix来实现不同动画,控件的实际位置没有该改变

Animation补间动画用法没那么灵活,扩展性也不好,取而代之的是属性动画

但是通过对android-Animation的分析和理解来拓展我们的思路

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。