Android source code analysis-how property animation works

Source: Internet
Author: User


Preface

This is the last article in the Android animation series. By analyzing the source code, we can deeply understand the working principle of property animation, which helps us better use property animation. However, because the underlying implementation of the animation has penetrated into the jni layer and involves the display subsystem, it is not only difficult but also meaningless to deeply analyze the underlying implementation of the animation, the analysis in this article ends with the jni layer.

 

Android animation series:

Android animation Overview

Android animation advanced-use the open source animation library nineoldandroids

In-depth analysis of Android property Animation: making you an animation cool

Android source code analysis-how property animation works

Principles of property Animation

Property animation requires that the set Method of the property be provided to the animation object. The property animation calls the set Method multiple times based on the familiar initial value and final value you pass, the values passed to the set method are different each time. Specifically, over time, the passed values become closer and closer to the final values. If the initial value is not transmitted during animation, The get method is also required because the system wants to get the initial value of the attribute. For attribute animation, there are so many operations in the animation process. Let's look at the source code analysis.

Source code analysis

First, we need to find an entry, starting with ObjectAnimator. ofInt (mButton, width, 500). setDuration (5000). start (). Other animations are similar.

Check the start method of ObjectAnimator.

 

    @Override    public void start() {        // See if any of the current active/pending animators need to be canceled        AnimationHandler handler = sAnimationHandler.get();        if (handler != null) {            int numAnims = handler.mAnimations.size();            for (int i = numAnims - 1; i >= 0; i--) {                if (handler.mAnimations.get(i) instanceof ObjectAnimator) {                    ObjectAnimator anim = (ObjectAnimator) handler.mAnimations.get(i);                    if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {                        anim.cancel();                    }                }            }            numAnims = handler.mPendingAnimations.size();            for (int i = numAnims - 1; i >= 0; i--) {                if (handler.mPendingAnimations.get(i) instanceof ObjectAnimator) {                    ObjectAnimator anim = (ObjectAnimator) handler.mPendingAnimations.get(i);                    if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {                        anim.cancel();                    }                }            }            numAnims = handler.mDelayedAnims.size();            for (int i = numAnims - 1; i >= 0; i--) {                if (handler.mDelayedAnims.get(i) instanceof ObjectAnimator) {                    ObjectAnimator anim = (ObjectAnimator) handler.mDelayedAnims.get(i);                    if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {                        anim.cancel();                    }                }            }        }        if (DBG) {            Log.d(ObjectAnimator, Anim target, duration:  + mTarget + ,  + getDuration());            for (int i = 0; i < mValues.length; ++i) {                PropertyValuesHolder pvh = mValues[i];                ArrayList
 
   keyframes = pvh.mKeyframeSet.mKeyframes;                Log.d(ObjectAnimator,    Values[ + i + ]:  +                    pvh.getPropertyName() + ,  + keyframes.get(0).getValue() + ,  +                    keyframes.get(pvh.mKeyframeSet.mNumKeyframes - 1).getValue());            }        }        super.start();    }
 
Note: The code above does not look so long. In fact, it is very simple to do. First, we will judge if the current animation, the waiting animation (Pending), and the delayed animation (Delay) if there is the same animation as the current one, cancel the same animation. The next part is log, and then the super of the parent class is called. the start () method. Because ObjectAnimator inherits ValueAnimator, let's take a look at the Start method of ValueAnimator.

 

 

    private void start(boolean playBackwards) {        if (Looper.myLooper() == null) {            throw new AndroidRuntimeException(Animators may only be run on Looper threads);        }        mPlayingBackwards = playBackwards;        mCurrentIteration = 0;        mPlayingState = STOPPED;        mStarted = true;        mStartedDelay = false;        mPaused = false;        AnimationHandler animationHandler = getOrCreateAnimationHandler();        animationHandler.mPendingAnimations.add(this);        if (mStartDelay == 0) {            // This sets the initial value of the animation, prior to actually starting it running            setCurrentPlayTime(0);            mPlayingState = STOPPED;            mRunning = true;            notifyStartListeners();        }        animationHandler.start();    }
Note: The above code will eventually call the start method of the AnimationHandler, which is not a Handler, but a Runnable. Looking at its code, we found through the code that it was soon called to the jni layer, but the jni layer still needs to be called back. Its run method will be called. This Runnable involves interaction with the underlying layer. We will ignore this part and focus on the doAnimationFrame method in ValueAnimator.

 

 

    final boolean doAnimationFrame(long frameTime) {        if (mPlayingState == STOPPED) {            mPlayingState = RUNNING;            if (mSeekTime < 0) {                mStartTime = frameTime;            } else {                mStartTime = frameTime - mSeekTime;                // Now that we're playing, reset the seek time                mSeekTime = -1;            }        }        if (mPaused) {            if (mPauseTime < 0) {                mPauseTime = frameTime;            }            return false;        } else if (mResumed) {            mResumed = false;            if (mPauseTime > 0) {                // Offset by the duration that the animation was paused                mStartTime += (frameTime - mPauseTime);            }        }        // The frame time might be before the start time during the first frame of        // an animation.  The current time must always be on or after the start        // time to avoid animating frames at negative time intervals.  In practice, this        // is very rare and only happens when seeking backwards.        final long currentTime = Math.max(frameTime, mStartTime);        return animationFrame(currentTime);    }
Note that the animationFrame method is called at the end of the above Code, while the animationFrame method calls the animateValue internally. The following describes the code of the 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);            }        }    }
In the above Code, the calculateValue method is used to calculate the attribute value corresponding to each frame of the animation. The following describes where to call the get and set methods of the attribute, after all, this is what we are most concerned about.

 

Get method: during initialization, if the initial value of the attribute is not provided, the get method will be called.

 

    private void setupValue(Object target, Keyframe kf) {        if (mProperty != null) {            kf.setValue(mProperty.get(target));        }        try {            if (mGetter == null) {                Class targetClass = target.getClass();                setupGetter(targetClass);                if (mGetter == null) {                    // Already logged the error - just return to avoid NPE                    return;                }            }            kf.setValue(mGetter.invoke(target));        } catch (InvocationTargetException e) {            Log.e(PropertyValuesHolder, e.toString());        } catch (IllegalAccessException e) {            Log.e(PropertyValuesHolder, e.toString());        }    }

 

Set Method: when the next frame of the animation arrives, the setAnimatedValue method in PropertyValuesHolder sets the new property value to the object and calls its set method.

 

    void setAnimatedValue(Object target) {        if (mProperty != null) {            mProperty.set(target, getAnimatedValue());        }        if (mSetter != null) {            try {                mTmpValueArray[0] = getAnimatedValue();                mSetter.invoke(target, mTmpValueArray);            } catch (InvocationTargetException e) {                Log.e(PropertyValuesHolder, e.toString());            } catch (IllegalAccessException e) {                Log.e(PropertyValuesHolder, e.toString());            }        }    }
To sum up, I think the logic of this source code analysis is a bit confusing and I hope it will not mislead you. From the source code point of view, the source code logic layer of the property animation is a little skip, but it doesn't matter. You only need to understand the working principle of the property animation, the purpose of the source code is to let us find that the working principle is indeed true. So far, the Android animation series have all been completed. Thank you very much for reading this article and hope to help you!

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.