android动画深入分析—补间动画

1,836 阅读4分钟

总述

android中的动画有帧动画,补间动画,属性动画。最近结合约束布局,也新增加了motionlayout实现动画,在anroid studio 4.0预览版,可以可视化编辑动画,感觉Android实现复杂的动画越来方便了。对于MotionLayout大家可以看官方的博客。平时我们用的比较多的是属性动画和帧动画,在自定义view时,经常用到属性动画。所以这里主要分析这两种动画

动画概况

8VS7bF.png

补间动画调用原理

调用流程

8VpSKK.png

核心调用逻辑

applyLegacyAnimation

在view的draw方法会判断是否有动画,调用该方法

if (a != null) {
            more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);
            concatMatrix = a.willChangeTransformationMatrix();
            if (concatMatrix) {
                mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
            }
            transformToApply = parent.getChildTransformation();
        }

  • 判断是否初始化,没有的化进行初始化配置,并回调onAnimationStart
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();       
        }

  • 计算返回给draw方法的判断标志more,注意其会传递一个Transformation
 final Transformation t = parent.getChildTransformation();
 boolean more = a.getTransformation(drawingTime, t, 1f);

然后根据more标志,进入下面的判断,

  • more为false,返回

more为true

  • 边界发生改变
  • 边界未发生改变
  • 最终都会调用parent.invlidate
  • 触发下一轮绘制
总结
  • 通过标示,判断,是否还有进行,如果要动画还未结束,首先判断是否边界发生改变,然后触发重绘,不断循环,直到结束。
Animation的getTransformation

返回某一时刻的Transformation

public boolean getTransformation(long currentTime, Transformation outTransformation) {
        //动画相对于开始的时间查(startTime)
        final long startOffset = getStartOffset();
        final long duration = mDuration;
        float normalizedTime;
        //如果有执行时间,计算当前动画执行的百分比
        if (duration != 0) {
            normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) /
                    (float) duration;
        } 
        ....
        
        //判断是否结束,一种是百分比为1,一种是取消动画
        final boolean expired = normalizedTime >= 1.0f || isCanceled();
        mMore = !expired;

        //是否考虑复原到动画执行之前,FillBefore
        if (!mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);

        if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) {
            ....
            //通过插值器修改百分比
            final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
            //根据新的百分比计算Transfromation
            applyTransformation(interpolatedTime, outTransformation);
        }

      // 动画执行完的处理,例如重复动画的处理,动画结束回调
        if (expired) {
            if (mRepeatCount == mRepeated || isCanceled()) {
                if (!mEnded) {
                    mEnded = true;
                    guard.close();
                    //动画结束回调
                    fireAnimationEnd();
                }
            } else {
              //重复执行动画
                if (mRepeatCount > 0) {
                    mRepeated++;
                }

                if (mRepeatMode == REVERSE) {
                    mCycleFlip = !mCycleFlip;
                }

                mStartTime = -1;
                mMore = true;

                fireAnimationRepeat();
            }
        }


        return mMore;
    }

总结
  • 该方法首先根据时间流逝计算百分比
  • 然后调用设置的插值其修改百分比
  • 接着,使用得到的百分比,去获取Transfromation
applyTransfromation
  • 其有具体的子类实现,我们看TranslateAnimation位移动画
  • 其实现比较简单,就是根据时间百分比得到位移的变量
  • 然后生成新的矩阵
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);                   

到这里,发现其实际是改变了Transfromation的matrix,那具体如何改变矩阵,view就发生位移了,既Transfromation在那里使用了,我们回到draw方法

 more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);
 concatMatrix = a.willChangeTransformationMatrix();

发现其在调用applyLegacyAnimation后,或获取concactmatrix这个主要是判断标志,判断矩阵是否发生改变

//判断动画是否改变了transformation的矩阵
/**
     * <p>Indicates whether or not this animation will affect the transformation
     * matrix. For instance, a fade animation will not affect the matrix whereas
     * a scale animation will.</p>
     *
     * @return true if this animation will change the transformation matrix
     */
    public boolean willChangeTransformationMatrix() {
        // assume we will change the matrix
        return true;
    }

  • 获取transformToApply,改变后的transformation
 transformToApply = parent.getChildTransformation();
  • 然后调用canvas矩阵绘制方法
canvas.translate(-transX, -transY);
canvas.concat(transformToApply.getMatrix());
canvas.translate(transX, transY);

总结

  • 补间动画没有改变view的属性,其通过改变绘制的矩阵,既Transfomation的矩阵实现动画,既改变绘制的内容,这也是为什么点击事件仍然是响应原来的地方。可以看作是两个坐标系,既canvas的坐标系改变了,但view的仍然是原来的。
  • 补间动画通过计算百分比即时间流逝的百分比是否为1,会返回一个more标志,然后调用重绘方法,是动画不断往前走
  • 在计算百分比是,其会调用我们设置的插值器修改它,默认为AccelerateDecelerateInterpolator.其在构造方法最后调用ensureInterpolator设置
protected void ensureInterpolator() {
        if (mInterpolator == null) {
            mInterpolator = new AccelerateDecelerateInterpolator();
        }
    }

限于篇幅,同时因为属性动画比较复杂,另外单独总结写一篇。这样看起来也方便写。