安卓动画(一)补间动画

最近项目上要实现一个动画,不过这个动画做起来比较难,一是要跟随手势,另外涉及到多个view,不能通过简单的属性动画实现,目前思路应该可以用Scene动画实现,但是又要做到跟手这就比较麻烦了。不过还好这个需求不急,趁这段时间把动画的整个框架过一遍,挖一挖动画的实现代码,说不定写完这个系列,我那个问题自然而然就解决了。

安卓动画主要分三种,补间动画、帧动画和属性动画,补间动画直接作用于view,也就是view和animation是紧密结合的,在view的绘制流程中就会判断当前view是否有animation,然后再做相应的操作,正因为和view结合的如此紧密,因此这种动画只能作用于view;帧动画就是整个动画期间不断刷新图片,这种动画需要多个图片,易导致内存占用过大的问题;属性动画是这三种动画中功能最强大的,这个动画其实相当于一个定时器,每隔一段时间就去处理我们监听的事件,由于这个间隔很短,如果在处理事件期间我们不断设置view的属性,那么看起来就像动画了,好了三种动画的基本情况就是如此,这一篇先从补间动画开始将。

系统为我们定义好了4种补间动画,translatescalerotatealpha.四种动画都有各自对应的类,它们的基类为Animation,如果我们想要定义自己的补间动画,只需要继承这个类,并且重写其applyTransformation方法,我们来看看系统为我们定义的补间动画这个方法的实现方式:

//TranslateAnimation
@Override
    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);
    }
	//ScaleAnimation
	@Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        float sx = 1.0f;
        float sy = 1.0f;
        float scale = getScaleFactor();

        if (mFromX != 1.0f || mToX != 1.0f) {
            sx = mFromX + ((mToX - mFromX) * interpolatedTime);
        }
        if (mFromY != 1.0f || mToY != 1.0f) {
            sy = mFromY + ((mToY - mFromY) * interpolatedTime);
        }

        if (mPivotX == 0 && mPivotY == 0) {
            t.getMatrix().setScale(sx, sy);
        } else {
            t.getMatrix().setScale(sx, sy, scale * mPivotX, scale * mPivotY);
        }
    }

看到上面的实现方式都是对Matrix进行操作,其实平移、旋转、缩放都是可以转换成矩阵运算的,这个矩阵位于Transformation这个类中。
关于上面动画的使用就不具体说了,还是比较简单的,还是探究一下实现原理比较好玩,第一点需要注意的是补间动画实际上不改变view的属性,效果都是画出来的,view自始至终的位置和其他属性根本没有变化,那么以这个为出发点,看安卓是如何实现的。
由ViewGroup.dispatchDraw方法切入,在这个方法中会依次遍历子view,通知子view去绘制自己,最终调用到

boolean draw(Canvas canvas, ViewGroup parent, long drawingTime)

这个方法,这里view还没有绘制自己,view绘制自身内容是在onDraw方法中,在这个方法中发现了和动画相关的信息:

//获取当前view的animation信息,view本身有个animation变量
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();
        }

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) {
        	//第一次进入,需要对动画进行初始化,确保动画animation知道view是布局在何处的
            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();
        //跟进去可以看到主要是根据时间决定动画进行到什么程度,然后调用anmation类中的applyTransformation方法,计算当前的matrix,再存进transformation中
        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;
        }
		//动画未进行完,那么more为true,这时就需要刷新界面了,保证动画继续进行
        if (more) {
	        //根据当前动画确定是否改变了位置,根据改变的位置去重新刷新,不过好像一般的view只能全部刷新,surfaceview可以局部刷新,一般来说动画都会改变位置
            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 requests
                    parent.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
                    parent.invalidate(mLeft, mTop, mRight, mBottom);
                }
            } else {
                if (parent.mInvalidateRegion == null) {
                    parent.mInvalidateRegion = new RectF();
                }
                final RectF region = parent.mInvalidateRegion;
                //计算改变的矩形,这也是根据动画中的matrix来计算的
                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 requests
                parent.mPrivateFlags |= PFLAG_DRAW_ANIMATION;

                final int left = mLeft + (int) region.left;
                final int top = mTop + (int) region.top;
                //最终调用invalidate方法,使得动画能在界面上动起来
                parent.invalidate(left, top, left + (int) (region.width() + .5f),
                        top + (int) (region.height() + .5f));
            }
        }
        return more;
    }

写到这里我们理顺了view是如何调用animation中applyTransformation方法的,但是动画是在哪里绘制的,这个还没找到具体代码的位置。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值