View Animation 运行原理解析Android View 绘制流程之 DecorView 与 ViewRootImplAndroid View 的绘制流程之 Measure 过程详解 (一)Android View 的绘制流程之 Layout 和

View Animation 运行原理解析Android View 绘制流程之 DecorView 与 ViewRootImplAndroid View 的绘制流程之 Measure 过程详解 (一)Android View 的绘制流程之 Layout 和,第1张

概述Android 平台目前提供了两大类动画,在 Android 3.0 之前,一大类是 View Animation,包括 Tween animation(补间动画),Frame animation(帧

AndroID 平台目前提供了两大类动画,在 AndroID 3.0 之前,一大类是 VIEw Animation,包括 Tween animation(补间动画),Frame animation(帧动画),在 AndroID 3.0 中又引入了一个新的动画系统:Property Animation,即属性动画。本篇文章主要介绍 VIEw Animation 的运行原理

VIEw Animation 可以使视图执行补间动画。即给定两个关键帧,然后中间部分按照一定的算法自动计算补充。

补间动画的继承关系:

 

下面看一个示例,比如想要对透明度做一个变化:

Animation AlphaAnimation = new AlphaAnimation(1,0);AlphaAnimation.setDuration(1000);imageVIEw.startAnimation(AlphaAnimation);@H_419_31@

 

 那么这个动画是如何实现的呢,下面就要讲述其实现原理。 

为了探究补间动画的实现原理,需要对相关源码进行解读,源码版本为 AndroID API 29 Platform。在解读之前,大家可以试着回答下面这些问题。

为什么移动位置后,点击事件的响应依旧是在原来位置上?

如果想知道动画的执行进度,是如何获取呢?

如果对 VIEw 做放大缩小得动画,那么其宽度高度值是否会变化。

相关类介绍

下面开始源码分析。首先看下基类 Animation。

public abstract class Animation implements Cloneable {}@H_419_31@

Animation 是一个抽象类,里面包含了各种动画相关的属性(时长,起始时间,重复次数,插值器等),回调(Listeners)。该类整体比较简单,大家直接看源码就好。

AlphaAnimation

下面来看下 Animation 子类,为了方便,本次就只说说 AlphaAnimation。

class AlphaAnimation extends Animation {    private float mFromAlpha;     mToAlpha;    /**     * Constructor used when an AlphaAnimation is loaded from a resource.      *      * @param context Application context to use     *  attrs Attribute set from which to read values     */    public AlphaAnimation(Context context,AttributeSet attrs) {        super(context,attrs);                TypedArray a =            context.obtainStyledAttributes(attrs,com.androID.internal.R.styleable.AlphaAnimation);                mFromAlpha = a.getfloat(com.androID.internal.R.styleable.AlphaAnimation_fromAlpha,1.0f);        mToAlpha = a.getfloat(com.androID.internal.R.styleable.AlphaAnimation_toAlpha,1)">);                a.recycle();    }             * Constructor to use when building an AlphaAnimation from code     *      *  fromAlpha Starting Alpha value for the animation,where 1.0 means     *        fully opaque and 0.0 means fully transparent.     *  toAlpha Ending Alpha value for the animation.     public AlphaAnimation(float fromAlpha, toAlpha) {        mFromAlpha = fromAlpha;        mToAlpha = toAlpha;    }             * Changes the Alpha property of the supplIEd {@link transformation} 
   * 实现动画的关键函数,这里通过当前的播放进度,计算当前的透明度,然后将其赋值给 transformation 实例
*/ @OverrIDe protected voID applytransformation( interpolatedTime,transformation t) { final float Alpha = mFromAlpha; t.setAlpha(Alpha + ((mToAlpha - Alpha) * interpolatedTime)); } @OverrIDe boolean willChangetransformationMatrix() { return false; } @OverrIDe // 不改变边界 willChangeBounds() { ; } * @hIDe hasAlpha() { true; }}@H_419_31@

整个代码也是很简单,其实很多逻辑都在基类处理了。然后子类只需要重写一些和自己动画相关的方法就好。其中 applytransformation 是实现某一种动画的关键,每个子类都必须重写。

这里需要注意的是,transformation 就是用来存储每一次动画的参数。其中平移,旋转,缩放都是通过改变 Matrix 实现的,而透明度则是改变 Alpha 值实现的。

为了便于大家进一步理解,可以在看看 AnimationSet。

AnimationSet

因为 AnimationSet 是其他几个子类得集合体,所以看看它的代码逻辑还是可以发现一些不一样的。其内部代码比较多,就不贴出来了。只是挑一部分讲下:

         * Add a child animation to this animation set.     * The transforms of the child animations are applIEd in the order     * that they were added     *  a Animation to add.     voID addAnimation(Animation a) {
     // 数组来保存动画 mAnimations.add(a);
boolean noMatrix = (mFlags & PROPERTY_MORPH_MATRIX_MASK) == 0; if (noMatrix && a.willChangetransformationMatrix()) { mFlags |= PROPERTY_MORPH_MATRIX_MASK; } boolean changeBounds = (mFlags & PROPERTY_CHANGE_BOUNDS_MASK) == 0; if (changeBounds && a.willChangeBounds()) { mFlags |= PROPERTY_CHANGE_BOUNDS_MASK; } if ((mFlags & PROPERTY_DURATION_MASK) == PROPERTY_DURATION_MASK) { mLastEnd = mStartOffset + mDuration; } else { if (mAnimations.size() == 1) { mDuration = a.getStartOffset() + a.getDuration(); mLastEnd = mStartOffset + mDuration; } { mLastEnd = Math.max(mLastEnd,mStartOffset + a.getStartOffset() + a.getDuration()); mDuration = mLastEnd - mStartOffset; } } mDirty = ; } * The transformation of an animation set is the concatenation of all of its * component animations. * * @see androID.vIEw.animation.Animation#gettransformation
   * true 表示动画还在运行
boolean gettransformation(long currentTime,1)">int count = mAnimations.size(); final ArrayList<Animation> animations = mAnimations; final transformation temp = mTemptransformation; boolean more = boolean started = boolean ended = ; t.clear(); for (int i = count - 1; i >= 0; --i) { final Animation a = animations.get(i);       // 清除上一个的数据 temp.clear();
       // 通过 temp 来获取每个 Animation 的 transformation more
= a.gettransformation(currentTime,temp,getScaleFactor()) || more;
       // 将各种动画参数组合在一起,注意 t 是引用对象,所以这里改了之后,外面拿到的也是改了的。 t.compose(temp); started
= started || a.hasstarted(); ended = a.hasEnded() && ended; }     // 是否开始了 if (started && !mStarted) { dispatchAnimationStart(); mStarted = ; } if (ended != mEnded) { dispatchAnimationEnd(); mEnded = ended; } return more; }@H_419_31@

上面是 AnimationSet 中我认为两个比较重要的方法:

addAnimation:将其他动画类型添加到 set 里面,内部实际上是通过一个 List 来保存的。然后将每个动画的各种属性都记录下。

gettransformation:获取每个动画的下一个动画参数,然后将其组合在一起。

前面介绍了 Animation 一些背景知识。到这里,大家多少会有一些认识了。接下去就按照调用流程来分析动画的执行。

源码解析VIEw.startAnimation()
     startAnimation(Animation animation) {
     // 传入的值是-1,代表准备动画了 animation.setStartTime(Animation.START_ON_FirsT_FRAME); setAnimation(animation); invalIDateParentCaches(); // 给 parent 的 mPrivateFlag 加了一个 PFLAG_INVALIDATED invalIDate(
); // 其目的就是将其和子 vIEw 的 drawing 缓存都标记为无效,然后可以 redrawn } setAnimation(Animation animation) {
     // VIEw 中有个属性是用来存储当前的 Animation 的 mCurrentAnimation
= animation; if (animation != null) { // If the screen is off assume the animation start time is Now instead of the next frame we draw. KeePing the START_ON_FirsT_FRAME start time would cause the animation to start when the screen turns back on if (mAttachInfo != null && mAttachInfo.mdisplayState == display.STATE_OFF && animation.getStartTime() == Animation.START_ON_FirsT_FRAME) { animation.setStartTime(AnimationUtils.currentAnimationTimeMillis()); } animation.reset(); } }@H_419_31@

 简而言之 startAnimation 主要是做这么几件事情:

开始动画前,先告知 Animation,可以做一些准备,包括部分参数的赋值;

更新 VIEw 自身的 Animation 的属性值;

给父 VIEw 的 mPrivateFlag 加上 PFLAG_INVALIDATED 属性;

将自身和子 vIEw 的 draw cache 都标记为无效的,通过 父 VIEw 调用 invalIDateChild 促发 redrawn;

VIEwGroup.invalIDateChild

下面看下 invalIDateChild 是怎么促发重绘的:

 VIEwGroup     voID invalIDateChild(VIEw child,1)">final Rect dirty) {        final AttachInfo attachInfo = mAttachInfo;        if (attachInfo != null && attachInfo.mHarDWareAccelerated) {             HW accelerated fast path            onDescendantInvalIDated(child,child);            ;        }        VIEwParent parent = this) {             ..... 省略非关键性代码             do { // 无限循环                VIEw vIEw = ;                if (parent instanceof VIEw) {                    vIEw = (VIEw) parent;                }                if (drawAnimation) {                    if (vIEw != ) {                        vIEw.mPrivateFlags |= PFLAG_DRAW_ANIMATION;                    } else  VIEwRootImpl) {                        ((VIEwRootImpl) parent).mIsAnimating = ;                    }                }                 If the parent is dirty opaque or not dirty,mark it dirty with the opaque                 flag coming from the child that initiated the invalIDate                ) {                    if ((vIEw.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) {                        vIEw.mPrivateFlags = (vIEw.mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DIRTY;                    }                }          // 最终调用的是该方法来重绘                parent = parent.invalIDateChildInParent(location,dirty);                  .... 省略非关键性代码             } while (parent != );  // 终止条件是找不到父 VIEw 了。        }    }@H_419_31@

这里很主要的一件事,找到 rooVIEw ,然后调用了 invalIDateChildInParent 方法。这里的 rootVIEw 其实就是 VIEwRootImpl,至于为啥不是 DecorVIEw,这个可以去看看这篇文章:

Android View 绘制流程之 DecorView 与 ViewRootImplVIEwRootImpl.invalIDateChildInParent
    @OverrIDe    public VIEwParent invalIDateChildInParent(int[] location,Rect dirty) {        checkThread();  // 检查是否是主线程        if (DEBUG_DRAW) Log.v(mTag,"InvalIDate child: " + dirty);        if (dirty == ) {  // 从上面路径来看,这里是不可能为空的            invalIDate();              ;        } if (dirty.isEmpty() && !mIsAnimating) {            ;        }        // ... 跳过一段无关的代码
invalIDateRectOnScreen(dirty); ; }@H_419_31@

这里可以看出的是,最终会调用 invalIDateRectOnScreen 方法。

VIEwRootImpl.invalIDateRectOnScreen
     invalIDateRectOnScreen(Rect dirty) {        final Rect localDirty = mDirty;         Add the new dirty rect to the current one 其实就是把两个矩阵融合在一起        localDirty.union(dirty.left,dirty.top,dirty.right,dirty.bottom);         Intersect with the bounds of the window to skip         updates that lIE outsIDe of the visible region        float appScale = mAttachInfo.mApplicationScale;
     // 主要就是检查菊矩阵边界对不对
boolean intersected = localDirty.intersect(0,0,(int) (mWIDth * appScale + 0.5f),(int) (mHeight * appScale + 0.5f));
     // 边界不对,就会直接置空
if (!intersected) { localDirty.setEmpty(); }
     // mWillDrawSoon 是当前是否马上就要开始绘制了,如果开始绘制,就不去发起绘制了
if (!mWillDrawSoon && (intersected || mIsAnimating)) { scheduleTraversals(); } }@H_419_31@

 invalIDateRectOnScreen 主要是就是把 dirty 这个矩阵和已有的进行融合,然后再看看需不需要发起刷新。

scheduleTraversals() 作用是将 performTraversals() 封装到一个 Runnable 里面,然后扔到 Choreographer 的待执行队列里,这些待执行的 Runnable 将会在最近的一个 16.6 ms 屏幕刷新信号到来的时候被执行。而 performTraversals() 是 VIEw 的三大 *** 作:测量、布局、绘制的发起者。

小结:

当调用了 VIEw.startAniamtion() 之后,动画并没有马上就被执行,这个方法只是做了一些变量初始化 *** 作,接着将 VIEw 和 Animation 绑定起来,然后调用重绘请求 *** 作,内部层层寻找 mParent,最终走到 VIEwRootImpl 的 scheduleTraversals 里发起一个遍历 VIEw 树的请求,这个请求会在最近的一个屏幕刷新信号到来的时候被执行,调用 performTraversals 从根布局 DecorVIEw 开始遍历 VIEw 树。

开始动画前面说到了动画的开始最终是通过促发 VIEw 绘制来形成的。此处不会再讲 VIEw 的绘制原理,不懂得可以看下面两篇文章:Android View 的绘制流程之 Measure 过程详解 (一)Android View 的绘制流程之 Layout 和 Draw 过程详解 (二)

那么在 VIEw  绘制过程中,是在哪里开始绘制的呢? 答案是 VIEw 的 draw 方法里面开始的。

但是这个 draw 不是我们自定义 vIEw 时常见的 draw 方法,该 draw 方法有三个参数,是用于 VIEw 自身绘制用的。

VIEw.draw

该方法比较长,截取部分来讲:

     * 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,1)"> drawingTime) {
     // 用于判断是否支持硬件加速 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; ..... 跳过一些代码

     // 获取之前存储的 animation     getAnimation(); if (a != ) {
       // 不为空就说明是有动画的 more
= applyLegacyAnimation(parent,drawingTime,a,scalingrequired); concatMatrix = a.willChangetransformationMatrix(); (concatMatrix) { mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_transform; }
       // 这里是拿到动画参数,后面会再次讲到 transformToApply
= parent.getChildtransformation(); } ...... 省略代码}@H_419_31@

首先来看这个 draw 方法的三个参数:

canvas:这个没有什么好分析的,是用来绘制用的

parent:这个其实就是父 VIEw,方便获取一些数据;

drawingTime:这个很重要,就是当前的绘制时间,后续做动画的时候,会计算时间差,然后更新插值器;

一进到 draw 方法,就先获取当前是否支持硬件加速。有硬件加速和没有硬件加速走的是两套逻辑。然后是获取保之前存储的 animation。

VIEw.applyLegacyAnimation

接着调用 applyLegacyAnimation 开始处理动画相关的逻辑。下面看下其方法内部的逻辑。

         * Utility function,called by draw(canvas,parent,drawingTime) to handle the less common     * case of an active Animation being run on the vIEw.     boolean applyLegacyAnimation(VIEwGroup parent,1)"> drawingTime,Animation a, scalingrequired) {
     // 用于保存此时重绘的变换 transformation invalIDationtransform;
int flags = parent.mGroupFlags; boolean initialized = a.isInitialized();
     // 判断动画有没有开始初始化,没有的化先进行初始化
initialized) { a.initialize(mRight - mleft,mBottom - mtop,parent.getWIDth(),parent.getHeight()); a.initializeInvalIDateRegion(0,mRight - mleft,1)"> mtop); ) a.setListenerHandler(mAttachInfo.mHandler);
       // 同时调用开始动画回调 onAnimationStart(); }     // 这里是从父类中获取当前的t,但是如果一个父类存在多个子 vIEw 需要运动,那获取的岂不是一样了?其实每个子vIEw 都会重新赋值,不会影响。
final transformation t = parent.getChildtransformation();
     // 这里是根据时间,t,缩放因子来计算 t,这里 t 是一个对象,在 animation 中进行赋值后,在这里也可以用到
boolean more = a.gettransformation(drawingTime,t,1f);
     // 对于需要缩放的子vIEw,需要重新计算t,可是调用方法确是一样的?那结果有啥不一样吗?这里是为了将缩放和不缩放的 t 分出来
if (scalingrequired && mAttachInfo.mApplicationScale != 1f) { if (parent.mInvalIDationtransformation == ) { parent.mInvalIDationtransformation = new transformation(); } invalIDationtransform = parent.mInvalIDationtransformation; a.gettransformation(drawingTime,invalIDationtransform,1f); } { invalIDationtransform = t; }     // more 为 true ,代表动画还未结束 (more) { a.willChangeBounds()) { if ((flags & (VIEwGroup.FLAG_OPTIMIZE_INVALIDATE | VIEwGroup.FLAG_ANIMATION_DONE)) == VIEwGroup.FLAG_OPTIMIZE_INVALIDATE) { parent.mGroupFlags |= VIEwGroup.FLAG_INVALIDATE_required; } if ((flags & VIEwGroup.FLAG_INVALIDATE_required) == 0 The child need to draw an animation,potentially offscreen,so make sure we do not cancel invalIDate requests parent.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
            // 发起下一次重绘 parent.invalIDate(mleft,mtop,mRight,mBottom); } }
{ if (parent.mInvalIDateRegion == ) { parent.mInvalIDateRegion = RectF(); } final RectF region = parent.mInvalIDateRegion;
          // 对于会改变自己边界的动画,比如缩放,这时候需要计算当前缩放的尺寸范围 a.getInvalIDateRegion(
0,region,invalIDationtransform); make sure we do not cancel invalIDate requests parent.mPrivateFlags |= PFLAG_DRAW_ANIMATION;          // region 此时是更新尺寸后的范围了 int left = mleft + () region.left; int top = mtop + () region.top;
         // 发起下一次重绘 parent.invalIDate(left,top,left
+ (int) (region.wIDth() + .5f),top + (int) (region.height() + .5f)); } } more; }@H_419_31@

这个方法其实理解起来也很简单,主要就是为了得到一个根据当前时间计算得到 transformation 实例,里面包含了下一次动画所需要的信息。

transformation 里面的内容如下:

transformation
class transformation {         * Indicates a transformation that has no effect (Alpha = 1 and IDentity matrix.)     static int TYPE_IDENTITY = 0x0;         * Indicates a transformation that applIEs an Alpha only (uses an IDentity matrix.)     int TYPE_Alpha = 0x1     * Indicates a transformation that applIEs a matrix only (Alpha = 1.)     int TYPE_MATRIX = 0x2     * Indicates a transformation that applIEs an Alpha and a matrix.     int TYPE_BOTH = TYPE_Alpha | TYPE_MATRIX;   // 矩阵,控制缩放,平移,旋转    protected Matrix mMatrix;
   // 透明度
mAlpha; mtransformationType; mHasClipRect; private Rect mClipRect = Rect(); ...... 省略一大串代码 }@H_419_31@

 

上述代码还省略很多方法,其实都是对矩阵的 *** 作。

这里提一下:Matrix 方法中的 setRotate() 方法会先清除该矩阵,即设为单位矩阵。之后设置旋转 *** 作的,同样,setTranslate() 等方法也是一样的。所以是不能叠加各种效果在一起的.如果是想多种效果同时使用的话,postRotate(),postTranslate()等类似的矩阵变换方法吧。

想进一步了解的可直接阅读代码。

下面讲下是如何获取 transformation 的。

Animation.gettransformation
         // 等于-1,说明是刚开始动画,记录第一帧动画时间        if (mStartTime == -1) {            mStartTime = currentTime;        }     // 相当于是延迟多少时间执行        long startOffset = getStartOffset();         long duration = mDuration;         normalizedTime;        if (duration != 0       // 归一化,也就是转化为百分比,当前动画进度            normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) /                    () duration;        }  time is a step-change with a zero duration            normalizedTime = currentTime < mStartTime ? 0.0f : 1.0fboolean expired = normalizedTime >= 1.0f || isCanceled();        mMore = !expired;     // 确保动画在 0-1 之间        if (!mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime,1.0f),0.0f);        if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) {            mStarted) {
          // 通知动画开始了。onAnimationStart 就是在这里被调用 fireAnimationStart(); mStarted
= (NoImagePreloadHolder.USE_CLOSEGUARD) { guard.open("cancel or detach or gettransformation"); } } if (mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime,1)">); (mCycleFlip) { normalizedTime = 1.0f - normalizedTime; }       // 根据进度获取当前插值器的值 float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
       // 这里 out 前缀就是这个是要传出去的,这个方法每个 Animation 子类都要自己实现,然后其实我们可以重写这个方法,把进度传出去你就知道当前动画的进度了 applytransformation(interpolatedTime,outtransformation); }     // 如果动画被取消或者已经完成了
(expired) { if (mRepeatCount == mRepeated || isCanceled()) { mEnded) { mEnded = ; guard.close();
            // 这里就是 onAnimationEnd 调用的地方 fireAnimationEnd(); } }
{
          // else 说明动画是重复的,这是需要计算重复次数,还有是不是无限循环的
if (mRepeatCount > 0) { mRepeated++; } if (mRepeatMode == REVERSE) { mCycleFlip = !mCycleFlip; } mStartTime = -1; mMore = ;          // 这里就是 onAnimationRepeat 调用的地方 fireAnimationRepeat(); } } if (!mMore && mOneMoreTime) { mOneMoreTime = ; mMore; }@H_419_31@

 

gettransformation 主要就是管理动画状态的。到底是开始(记录开始时间),还是正在进行(计算进度),还是已经结束了(通知结束了)。

其中调用的 applytransformation,每个 Animation 子类都要自己实现,然后其实我们可以重写这个方法,把进度传出去你就知道当前动画的进度了。子类其实是把最后的计算结果保存在 transformation 里面了,这样就拿到了下一帧动画参数。

还有大家平时用到的 AnimationListener 也是在这里进行通知回调的。

那拿到 transformation 后,是怎么用的呢,这个就得 回到 vIEw.draw 方法了。

vIEw.draw

前面讲到了 transformation 其实是从 parent 中获取,赋值给 transformToApply;

     drawingTime) {             ...... 省略一大部分代码        float Alpha = drawingWithRenderNode ? 1 : (getAlpha() * getTransitionAlpha());
     // 下面这个if 会进入动画的真正的绘制时期
if (transformToApply != null || Alpha < 1 || !hasIDentityMatrix() || (mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_Alpha) != 0null || !childHasIDentityMatrix) { int transX = 0int transY = 0; (offsetForScroll) { transX = -sx; transY = -sy; } (concatMatrix) {
               // 为TRUE,代表是使用硬件加速来进行绘制
(drawingWithRenderNode) { renderNode.setAnimationMatrix(transformToApply.getMatrix()); } { Undo the scroll translation,apply the transformation matrix,1)"> 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; } } if (!childHasIDentityMatrix && !drawingWithRenderNode) { canvas.translate(-transX,1)">transY); canvas.concat(getMatrix()); canvas.translate(transX,transY); } } Deal with Alpha if it is or used to be <1 if (Alpha < 1 || (mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_Alpha) != 0) { if (Alpha < 1) { mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_Alpha; } { mPrivateFlags3 &= ~PFLAG3_VIEW_IS_ANIMATING_Alpha; } parent.mGroupFlags |= VIEwGroup.FLAG_CLEAR_transformATION; drawingWithDrawingCache) { int multiplIEdAlpha = (int) (255 * Alpha); onSetAlpha(multiplIEdAlpha)) { (drawingWithRenderNode) { renderNode.setAlpha(Alpha * getAlpha() * getTransitionAlpha()); } if (layerType == LAYER_TYPE_NONE) { canvas.saveLayerAlpha(sx,sy,sx + getWIDth(),sy + getHeight(),multiplIEdAlpha); } } { Alpha is handled by the child directly,clobber the layer's Alpha mPrivateFlags |= PFLAG_Alpha_SET; } } } } if ((mPrivateFlags & PFLAG_Alpha_SET) == PFLAG_Alpha_SET) { onSetAlpha(255); mPrivateFlags &= ~PFLAG_Alpha_SET; } @H_419_31@

那么对于 AnimationSet 又是如何处理的呢?

首先他也是继承了了 Animation,其次,它有个数组装门用来存放 Animation 集合。也是通过  gettransformation 来获取transformation的。

AnimationSet.

     animations.get(i);            temp.clear();            more = a.gettransformation(currentTime,1)"> more;            t.compose(temp);            started = started || more;    }@H_419_31@

通过 for 循环,依次获取对应的 annimation 的矩阵,然后再将矩阵效果合到一起。

到此,对于 动画应该是有自己的认识了。 VIEw Animation 的整个执行逻辑也就讲完了。

 

总结

那么这里回答开头的三个问题:

为什么移动位置后,点击事件的响应依旧是在原来位置上?

因为动画是在 draw 时候形成的,也就是说只是视觉效果。其并没有改变它本身在父类中的位置;

如果想知道动画的执行进度,是如何获取呢?

继承 Animation 对应的子类,然后重写 applytransformation 方法,就可以从中获取到进度。

如果对 VIEw 做放大缩小得动画,那么其宽度高度值是否会变化。

动画发生在 draw 时期,并不会改变测量结果

 

VIEw Animation 是在绘制的时候,改变 vIEw 的视觉效果来实现动画的。所以不会对 vIEw 的测量和布局过程有影响。

VIEw 的动画是通过触发绘制过程来执行 draw 的。因为动画是连续的,所以需要不停的触发。

 

 

参考文章:

View 动画 Animation 运行原理解析

 

 

总结

以上是内存溢出为你收集整理的View Animation 运行原理解析 Android View 绘制流程之 DecorView 与 ViewRootImplAndroid View 的绘制流程之 Measure 过程详解 (一)Android View 的绘制流程之 Layout 和 Draw 过程详解 (二)全部内容,希望文章能够帮你解决View Animation 运行原理解析 Android View 绘制流程之 DecorView 与 ViewRootImplAndroid View 的绘制流程之 Measure 过程详解 (一)Android View 的绘制流程之 Layout 和 Draw 过程详解 (二)所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1121333.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-29
下一篇 2022-05-29

发表评论

登录后才能评论

评论列表(0条)

保存