Skip to content

rickgit/Animation

Repository files navigation

动画

定义

指由许多帧静止的画面,以一定的速度(如每秒16张)连续播放时,肉眼因视觉残象产生错觉,而误以为画面活动的作品。。这是wiki百科的定义,但在实际开发中,手机是以每秒60帧进行绘制元素,保证动画的流畅度。

优化

動畫的12項基本法則

Android 2D动画

补间动画(AnimationDrawable)

官方定义:加载一系列的Drawable,一个接着另一个,组成动画

xml: <animation-list> item 元素的android:duration属性设置播放的时间,单位为毫秒(ms)

java: android.graphics.drawable.AnimationDrawable view.setBackgroundResource(R.drawable.animlist);

DrawableAnimation源码分析

1.构造方法,默认构造方法没有state,res为null

    private AnimationDrawable(AnimationState state, Resources res) {
        final AnimationState as = new AnimationState(state, this, res);
        setConstantState(as);
        if (state != null) {
            setFrame(0, true, false);
        }
    }

android.graphics.drawable.AnimationDrawable.AnimationState 继承android.graphics.drawable.DrawableContainer.DrawableContainerState AnimationState构造方法主要是对内部对象android.graphics.drawable.DrawableContainer.DrawableContainerState#mOwner的拷贝

    public abstract static class DrawableContainerState extends ConstantState {
        final DrawableContainer mOwner;
        

setConStatState方法将as保持到对象mAnimationState。

2.addFrame方法,将当前的frame保存到mAnimationState。并把frame设置为invisable

    public void addFrame(@NonNull Drawable frame, int duration) {
        mAnimationState.addFrame(frame, duration);
        if (!mRunning) {
            setFrame(0, true, false);
        }
    }

android.graphics.drawable.AnimationDrawable.AnimationState#addFrame

        public void addFrame(Drawable dr, int dur) {
            int pos = super.addChild(dr);
            mDurations[pos] = dur;
        }

super.addChild(dr),保存了drawable并设置为不可见,保存drawable是个数组,每次容器大小不够,用System.arraycopy(mDrawables, 0, newDrawables, 0, oldSize);增长10个单位。
接下来看下AnimationDrawable的setFrame方法

    private void setFrame(int frame, boolean unschedule, boolean animate) {
        if (frame >= mAnimationState.getChildCount()) {
            return;
        }
        mAnimating = animate;
        mCurFrame = frame;
        selectDrawable(frame);
        if (unschedule || animate) {
            unscheduleSelf(this);
        }
        if (animate) {
            // Unscheduling may have clobbered these values; restore them
            mCurFrame = frame;
            mRunning = true;
            scheduleSelf(this, SystemClock.uptimeMillis() + mAnimationState.mDurations[frame]);
        }
    }

selectDrawable根据上面的addFrame方法,可知frame为0,unscheduleSelf方法调用Drawable的消息回调接口。

  1. start方法
    public void start() {
        mAnimating = true;
        if (!isRunning()) {
            // Start from 0th frame.
            setFrame(0, false, mAnimationState.getChildCount() > 1
                    || !mAnimationState.mOneShot);
        }
    }

setFrame可以看,步骤2的分析。调用了android.graphics.drawable.Drawable#scheduleSelf方法,最终执行android.graphics.drawable.AnimationDrawable#run方法。run方法只有一行代码nextFrame(false),如下代码块。nextFrame增加1,再循环下一帧。

    private void nextFrame(boolean unschedule) {
        int nextFrame = mCurFrame + 1;
        final int numFrames = mAnimationState.getChildCount();
        final boolean isLastFrame = mAnimationState.mOneShot && nextFrame >= (numFrames - 1);

        // Loop if necessary. One-shot animations should never hit this case.
        if (!mAnimationState.mOneShot && nextFrame >= numFrames) {
            nextFrame = 0;
        }

        setFrame(nextFrame, unschedule, !isLastFrame);
    }

视图动画(ViewAnimation)

官方定义:使用视图动画系统(view animation system)执行View的补间动画。补间通过计算start point, end point, size, rotation的信息执行动画

xml:

    <alpha>, <scale>, <translate>, <rotate>,<set>

    <layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android" 
    android:animation="@anim/list_item_alpha" 
    android:animationOrder="normal" 
    android:delay="0.8" />

java: android.view.animation.AnimationSet, android.view.animation.Animation, android.view.animation.AnimationUtils,
android.view.animation.LayoutAnimationController 实现使多个控件按顺序一个一个的显示。

Animation源码分析

android.view.animation.Animation

  1. AlphaAnimation构造方法
    public AlphaAnimation(float fromAlpha, float toAlpha) {
        mFromAlpha = fromAlpha;
        mToAlpha = toAlpha;
    }
    
  1. start方法
    调用android.view.animation.Animation#setStartTime方法
    /**
     * When this animation should start. When the start time is set to
     * {@link #START_ON_FIRST_FRAME}, the animation will start the first time
     * {@link #getTransformation(long, Transformation)} is invoked. The time passed
     * to this method should be obtained by calling
     * {@link AnimationUtils#currentAnimationTimeMillis()} instead of
     * {@link System#currentTimeMillis()}.
     *
     * @param startTimeMillis the start time in milliseconds
     */
    public void setStartTime(long startTimeMillis) {
        mStartTime = startTimeMillis;
        mStarted = mEnded = false;
        mCycleFlip = false;
        mRepeated = 0;
        mMore = true;
    }

注释提示会调用#getTransformation(long, Transformation),在View.Draw时候的执行android.view.View#applyLegacyAnimation会调用到

draw()绘制的元素
         *      1. Draw the background
         *      2. If necessary, save the canvas' layers to prepare for fading
         *      3. Draw view's content
         *      4. Draw children
         *      5. If necessary, draw the fading edges and restore layers
         *      6. Draw decorations (scrollbars for instance)
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();
        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;
        }

a.setListenerHandler(mAttachInfo.mHandler);设置了Animation的handler,进行事件处理
可以看出是运用Transform进行对象的设置改变的
android.view.animation.AlphaAnimation#applyTransformation

    /**
     * Changes the alpha property of the supplied {@link Transformation}
     */
    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        final float alpha = mFromAlpha;
        t.setAlpha(alpha + ((mToAlpha - alpha) * interpolatedTime));
    }

先看下android.view.animation.Transformation含有两个重要属性

    protected Matrix mMatrix;
    protected float mAlpha;

这两个属性就是属性动画包换的视图转换信息

  1. cancel方法
    public void cancel() {
        if (mStarted && !mEnded) {
            fireAnimationEnd();
            mEnded = true;
            guard.close();
        }
        // Make sure we move the animation to the end
        mStartTime = Long.MIN_VALUE;
        mMore = mOneMoreTime = false;
    }

AnimationUtils将xml动画文件转换为Animation

 public static Animation loadAnimation(Context context, @AnimRes int id)
            throws NotFoundException {

        XmlResourceParser parser = null;
        try {
            parser = context.getResources().getAnimation(id);
            return createAnimationFromXml(context, parser);

最终调用android.view.animation.AnimationUtils#createAnimationFromXml(android.content.Context, org.xmlpull.v1.XmlPullParser, android.view.animation.AnimationSet, android.util.AttributeSet),转化为Animation对象

    private static Animation createAnimationFromXml(Context c, XmlPullParser parser,
            AnimationSet parent, AttributeSet attrs) throws XmlPullParserException, IOException {

        Animation anim = null;

        // Make sure we are on a start tag.
        int type;
        int depth = parser.getDepth();

        while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
               && type != XmlPullParser.END_DOCUMENT) {

            if (type != XmlPullParser.START_TAG) {
                continue;
            }

            String  name = parser.getName();

            if (name.equals("set")) {
                anim = new AnimationSet(c, attrs);
                createAnimationFromXml(c, parser, (AnimationSet)anim, attrs);
            } else if (name.equals("alpha")) {
                anim = new AlphaAnimation(c, attrs);
            } else if (name.equals("scale")) {
                anim = new ScaleAnimation(c, attrs);
            }  else if (name.equals("rotate")) {
                anim = new RotateAnimation(c, attrs);
            }  else if (name.equals("translate")) {
                anim = new TranslateAnimation(c, attrs);
            } else {
                throw new RuntimeException("Unknown animation name: " + parser.getName());
            }

            if (parent != null) {
                parent.addAnimation(anim);
            }
        }

        return anim;

    }

属性动画(PropertyAnimation Android 3.0,API level 11)

官方定义:通过一段时间改变任何Object的属性形成动画 TODO 插值器(Interpolator)和估值器(TypeEvaluator)分析

java:
android.animation.ObjectAnimator
android.animation.Animator
android.animation.AnimatorSet
android.animation.ValueAnimator
TimeInterpolator
android.view.ViewPropertyAnimator 封装了ValueAnimator

Duration:一个动画的时长,默认300ms
Time interpolation: 一段代码块计算当前的属性的值
Frame refresh delay:刷新的时间,默认10ms

ValueAnimator

通过指定一系列类型(int, float, or color)的值,使这些类型的值动态变动

ObjectAnimator

ObjectAnimator是ValueAnimator的子类,包含分时引擎和动画值计算,使目标对象的相应属性产生动画变动

ObjectAnimator源码分析

创建ObjectAnimator
  1. ObjectAnimator.ofInt(Object obj,String property,int ... intVals)返回ObjectAnimator对象 ObjectorAnimation主要是个工厂类,可以创建Int,float,object,argb类型的属性动画。
    public static ObjectAnimator ofInt(Object target, String propertyName, int... values) {
        ObjectAnimator anim = new ObjectAnimator(target, propertyName);
        anim.setIntValues(values);
        return anim;
    }

方法里面创建ObjectAnimator对象,

    private ObjectAnimator(Object target, String propertyName) {
        setTarget(target);
        setPropertyName(propertyName);
    }

ObjectorAnimator构造方法里面,初始化属性的方法android.animation.ObjectAnimator#setTarget和android.animation.ObjectAnimator#setPropertyName,mTarget是个参数obj弱引用类型的属性(WeakReference),设置mTarget时候,如果和原先的obj不同则执行代码,暂停动画,重新初始化obj;setPropertyName方法,获取mValues数组的第一个值,该数组是个android.animation.PropertyValuesHolder类型数组,这个属性待会会详细说明,先看下setPropertyName将获取mValues数组第一个元素属性字符串,android.animation.ValueAnimator#mValuesMap移除属性字符串的key,设置成新的动画属性字符串,值是mValues的第一个元素。


  1. 接着ObjectAnimation.ofInt(Object obj,String property,int ... intVals) 执行完ObjectValue构造方法并调用其android.animation.ObjectAnimator#setIntValues(PropertyValuesHolder... values),初始化mValues和mValuesMap
    @Override
    public void setIntValues(int... values) {
        if (mValues == null || mValues.length == 0) {
            // No values yet - this animator is being constructed piecemeal. Init the values with
            // whatever the current propertyName is
            if (mProperty != null) {
                setValues(PropertyValuesHolder.ofInt(mProperty, values));
            } else {
                setValues(PropertyValuesHolder.ofInt(mPropertyName, values));
            }
        } else {
            super.setIntValues(values);
        }
    }

2.1 intVals的数据封装为PropertyValuesHolder.ofInt(mProperty, values)的形式作为元素保存在属性mValues数组里面。mValues可以看出是保存一系列属性及其动画值的PropertyValuesHolder数组, PropertyValuesHolder的ofInt只是简单创建了IntPropertyValuesHolder对象的一行代码; IntPropertyValuesHolder继承了PropertyValuesHolder,构造方法调用父类的setIntValues,设置intVals以KeyframeSet.ofInt(int ... intVals)保存在android.animation.PropertyValuesHolder#mKeyframes中。

    public void setValues(PropertyValuesHolder... values) {
        int numValues = values.length;
        mValues = values;
        mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
        for (int i = 0; i < numValues; ++i) {
            PropertyValuesHolder valuesHolder = values[i];
            mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
        }
        // New property/values/target should cause re-initialization prior to starting
        mInitialized = false;
    }

2.2 mValuesMap存储property字符串为key,以PropertyValuesHolder为值的数据,方便根据属性字符串,索引动画值。
3.android.animation.ValueAnimator#setInterpolator方法 设置android.animation.ValueAnimator#mInterpolator的属性,默认是android.view.animation.AccelerateDecelerateInterpolator,调用方法时,如果为null则设置为android.view.animation.LinearInterpolator。该属性是在android.animation.ValueAnimator#android.animation.PropertyValuesHolder#setAnimatedValue。


4. android.animation.ValueAnimator#setEvaluator方法 在Animator里只有ofApla方法和ofObject有用到,该方法根据设置mValues第一个元素PropertyValuesHolder的android.animation.PropertyValuesHolder#mEvaluator属性和步骤2.1的KeyframeSet的android.animation.KeyframeSet#mEvaluator属性。
启动ObjectAnimator动画
  1. android.animation.ObjectAnimator#start方法
    从android.animation.ValueAnimator#sAnimationHandler(java.lang.ThreadLocal)调用一个android.animation.ValueAnimator.AnimatorHandler(android.animation.ValueAnimator.AnimatorHandler)的类型的对象,保证每个线程有个对应的AnimationHandler,Animatiorndler用来循环动画的类。
    如果没从ThreadLocal获取到AnimationHandler,则android.animation.ValueAnimator#start()。
    如果有,则取消AnimatorHandler里面的所有Animator(android.animation.ValueAnimator.AnimationHandler#mAnimations,android.animation.ValueAnimator.AnimationHandler#mPendingAnimations,android.animation.ValueAnimator.AnimationHandler#mDelayedAnims)动画执行,调用Animator的cancel方法

接着说下android.animation.ValueAnimator#start(),方法里面只调用了方法,start(false)。这个方法初始化和启动android.animation.ValueAnimator.AnimationHandler start方法。

        AnimationHandler animationHandler = getOrCreateAnimationHandler();
        animationHandler.mPendingAnimations.add(this);
        if (mStartDelay == 0) {
            // This sets the initial value of the animation, prior to actually starting it running
            if (prevPlayingState != SEEKED) {
                setCurrentPlayTime(0);
            }
            mPlayingState = STOPPED;
            mRunning = true;
            notifyStartListeners();
        }
        animationHandler.start();

如果马上执行的话,调用setCurrentPlayTime方法,更新属性。这个方法里面通过调用android.animation.ValueAnimator#setCurrentFraction获取动画因fraction,然后以参数的形式传入android.animation.ObjectAnimator#animateValue,反射更新动画。

    void animateValue(float fraction) {
        final Object target = getTarget();
        if (mTarget != null && target == null) {
            // We lost the target reference, cancel and clean up.
            cancel();
            return;
        }

        super.animateValue(fraction);
        int numValues = mValues.length;
        for (int i = 0; i < numValues; ++i) {
            mValues[i].setAnimatedValue(target);
        }
    }

在super.animateValue(fraction);时候获取值

    //android.animation.ValueAnimator#animateValue
    void animateValue(float fraction) {
        fraction = mInterpolator.getInterpolation(fraction);
        mCurrentFraction = fraction;
        int numValues = mValues.length;
        for (int i = 0; i < numValues; ++i) {
            mValues[i].calculateValue(fraction);
        }
        if (mUpdateListeners != null) {
            int numListeners = mUpdateListeners.size();
            for (int i = 0; i < numListeners; ++i) {
                mUpdateListeners.get(i).onAnimationUpdate(this);
            }
        }
    }

mValues[i].calculateValue(fraction);最总执行KeyFrameSet通过估值器获取值,参数fraction是通过插值器(时间分数=执行的时间/总时间)

    //android.animation.KeyframeSet#getValue
    public Object getValue(float fraction) {
        // Special-case optimization for the common case of only two keyframes
        if (mNumKeyframes == 2) {
            if (mInterpolator != null) {
                fraction = mInterpolator.getInterpolation(fraction);
            }
            return mEvaluator.evaluate(fraction, mFirstKeyframe.getValue(),
                    mLastKeyframe.getValue());
        }
        ···
        ···
        ···
    }

mValues[i].setAnimatedValue(target);这段代码通过反射,更新了对象的属性值。
接下来看android.animation.ValueAnimator.AnimationHandler#start方法。android.animation.ValueAnimator.AnimationHandler#mChoreographer调用android.animation.ValueAnimator.AnimationHandler#mAnimate,执行doAnimationFrame

        void doAnimationFrame(long frameTime) {
            mLastFrameTime = frameTime;

            // mPendingAnimations holds any animations that have requested to be started
            // We're going to clear mPendingAnimations, but starting animation may
            // cause more to be added to the pending list (for example, if one animation
            // starting triggers another starting). So we loop until mPendingAnimations
            // is empty.
            while (mPendingAnimations.size() > 0) {
                ArrayList<ValueAnimator> pendingCopy =
                        (ArrayList<ValueAnimator>) mPendingAnimations.clone();
                mPendingAnimations.clear();
                int count = pendingCopy.size();
                for (int i = 0; i < count; ++i) {
                    ValueAnimator anim = pendingCopy.get(i);
                    // If the animation has a startDelay, place it on the delayed list
                    if (anim.mStartDelay == 0) {
                        anim.startAnimation(this);
                    } else {
                        mDelayedAnims.add(anim);
                    }
                }
            }

android.animation.ValueAnimator.AnimationHandler#scheduleAnimation调用android.animation.ValueAnimator.AnimationHandler#mChoreographer#postCallback方法。android.animation.ValueAnimator.AnimationHandler#doAnimationFrame,通知界面android.animation.ValueAnimator#startAnimation
,执行android.animation.ValueAnimator#animationFrame(在这调用插值器),调用android.animation.ValueAnimator#animateValue更新target的数据。

  1. 该方法执行android.animation.ValueAnimator.AnimationHandler#mPendingAnimations的所有动画;

  2. 如果没有延迟执行startAnimation,否则加入的android.animation.ValueAnimator.AnimationHandler#mDelayedAnims里面。

            // Next, process animations currently sitting on the delayed queue, adding
            // them to the active animations if they are ready
            int numDelayedAnims = mDelayedAnims.size();
            for (int i = 0; i < numDelayedAnims; ++i) {
                ValueAnimator anim = mDelayedAnims.get(i);
                if (anim.delayedAnimationFrame(frameTime)) {
                    mReadyAnims.add(anim);
                }
            }
            int numReadyAnims = mReadyAnims.size();
            if (numReadyAnims > 0) {
                for (int i = 0; i < numReadyAnims; ++i) {
                    ValueAnimator anim = mReadyAnims.get(i);
                    anim.startAnimation(this);
                    anim.mRunning = true;
                    mDelayedAnims.remove(anim);
                }
                mReadyAnims.clear();
            }
  1. 接着遍历延迟动画列表,将可以准备好执行的动画加入mReadyAnims,并开始执行mReadyAnims的animstor,清空相关的延迟animator数据。
            // Now process all active animations. The return value from animationFrame()
            // tells the handler whether it should now be ended
            int numAnims = mAnimations.size();
            for (int i = 0; i < numAnims; ++i) {
                mTmpAnimations.add(mAnimations.get(i));
            }
            for (int i = 0; i < numAnims; ++i) {
                ValueAnimator anim = mTmpAnimations.get(i);
                if (mAnimations.contains(anim) && anim.doAnimationFrame(frameTime)) {
                    mEndingAnims.add(anim);
                }
            }
            mTmpAnimations.clear();
            if (mEndingAnims.size() > 0) {
                for (int i = 0; i < mEndingAnims.size(); ++i) {
                    mEndingAnims.get(i).endAnimation(this);
                }
                mEndingAnims.clear();
            }
  1. 遍历所有激活的动画,判断是否结束(android.animation.ValueAnimator#doAnimationFrame方法判断是否结束动画),并把结束的animator做处理,处理为于android.animation.ValueAnimator#endAnimation方法,并将改animator移除AnimatorHandler,发送通知android.animation.Animator.AnimatorListener#onAnimationEnd。通过android.animation.ValueAnimator#animationFrame更新界面。
            // Schedule final commit for the frame.
            mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, mCommit, null);
  1. 调用android.animation.ValueAnimator.AnimationHandler#commitAnimationFrame,调用激活的animator的commitAnimationFrame方法
            // If there are still active or delayed animations, schedule a future call to
            // onAnimate to process the next frame of the animations.
            if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) {
                scheduleAnimation();
            }
  1. 如果有animator未执行,则继续调用scheduleAnimation方法。

#####取消ObjectAnimator动画 android.animation.ValueAnimator#cancel,如果没有启动的animator,先执行android.animation.Animator.AnimatorListener#onAnimationStart方法,然后发送通知给监听对象android.animation.Animator.AnimatorListener#onAnimationCancel,最后android.animation.ValueAnimator#endAnimation,清除当前AnimatorHandler的所有动画信息。 ###android.view.Choreographer

ViewPager切换动画

Path动画

Mesh表示网格,说得通俗一点,可以将画板想像成一张格子布,在这个张布上绘制图片。Android 吸入动画效果详解 构建Mesh Path Canvas.drawBitmapMesh

canvas.drawBitmapMesh(mBitmap,
        mInhaleMesh.getWidth(),
        mInhaleMesh.getHeight(),
        mInhaleMesh.getVertices(),
        0, null, 0, mPaint);

Android 3D动画

android.graphics.Camera

案例结构

AnimationFactory.java

Animation
|____src
| |____main
| | |____AndroidManifest.xml
| | |____java
| | | |____edu
| | | | |____ptu
| | | | | |____androidanimation
| | | | | | |____AnimationActivity.java
| | | | | | |____animation
| | | | | | | |____AnimationFactory.java  #抽象工厂实现,可以返回ViewAnimation,AnimationDrawable,Animator动画类
| | | | | | | |____CustomAnimation.java
| | | | | | |____graphics
| | | | | | | |____PathUtils.java
| | | | | | |____viewpager
| | | | | | | |____ColorFragment.java
| | | | | | | |____impl
| | | | | | | | |____CustomTransformer.java
| | | | | | | |____ViewPagerFactory.java


CustomView
|____src
| |____main
| | |____AndroidManifest.xml
| | |____java
| | | |____edu
| | | | |____ptu
| | | | | |____customview
| | | | | | |____CustomAbstractView.java
| | | | | | |____CustomCompositeView.java
| | | | | | |____MainActivity.java
| | | | | | |____CustomDrawView.java
| | | | | | |____element
| | | | | | | |____impl  
| | | | | | | | |____LineChart.java
| | | | | | | | |____PathInfo.java    #绘图元素实现类,路径绘制( element implement)
| | | | | | | |____IDrawElement.java  #绘图元素接口(drawable element interface)

About

android animation

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages