Android Tween animation implementation framework

Source: Internet
Author: User
Tags time in milliseconds

Android Tween animation implementation framework

When I was writing a program, I encountered several Tween animation problems:

1. When an animation is executed, the click event is still at the starting position of the animation?

2. What does the value in the constructor parameter of XXXAnimation mean?

3. What is the difference between the negative values of fromDegrees and toDegrees in fromXValue and toXValue rotation animation in translation animation ?? (I believe many people have doubts)

4. How do four parameters of RotateAnimation: int ready txtype, float ready txvalue, int ready tytype, and float ready tyvalue determine the rotation origin? Where is the exact rotation origin?

Android animations are divided:

The Tween Animation View Animation is also called a compensation Animation.

Drawable Animation is also called Frame Animation.

Property Animation (join later than 3.0)

Mainly studying Tween animation

When I write a program, I often fail to get the desired effect due to improper parameter settings (mainly from how many degrees of rotation to how many degrees, sometimes negative degrees. So I plan to study the animation implementation framework.

Before the study, please read this article: the principle of image transformation Matrix in Android to learn about Matrix. To understand the various conversions of graphs, You need to obtain the corresponding transformation matrix.


First, let's take a look at the rough frame rendering process of the animation. Otherwise, I may be dizzy due to a mess of writing.

Call startAnimation to set the animation associated with the View, and then re-paint the View. Call drawChild when re-painting the View. In this case, obtain the Animation bound to the View. It is not null, as long as the animation time does not end, the transformation matrix will be obtained through the drawing time, and then the canvas origin is translated to the upper left corner of the view? (Is that true ?) Draw a new frame. After the painting, it will be re-painted, and then get the conversion matrix of a new frame ..... Loop down until the animation ends and the view is no longer re-painted.

Return to the onDraw function of View, and the onDraw function does the following work.

1. Draw the background

2. If necessary, save the canvas 'layers toprepare for fading

3. Draw view's content

4. Draw children

5. If necessary, draw the fading edges andrestore layers

6. Draw decorations (scrollbars forinstance)

When it is ViewGroup, the fourth step is executed, dispatchDraw (canvas );

@ Overrideprotected void dispatchDraw (Canvas canvas) {// LayoutAnimationController is familiar with the animation effect of the child controls of ViewGroup. It has not been found before. Final LayoutAnimationController controller = mLayoutAnimationController;... // We will draw our child's animation, let's reset the flag // process the child View animation. MPrivateFlags & = ~ DRAW_ANIMATION; mGroupFlags & = ~ FLAG_INVALIDATE_REQUIRED; boolean more = false; final long drawingTime = getDrawingTime (); if (flags & FLAG_USE_CHILD_DRAWING_ORDER) = 0) {for (int I = 0; I <count; I ++) {final View child = children [I]; if (child. mViewFlags & VISIBILITY_MASK) = VISIBLE | child. getAnimation ()! = Null) {more | = drawChild (canvas, child, drawingTime) ;}} else {for (int I = 0; I <count; I ++) {final View child = children [getChildDrawingOrder (count, I)]; if (child. mViewFlags & VISIBILITY_MASK) = VISIBLE | child. getAnimation ()! = Null) {more | = drawChild (canvas, child, drawingTime );}}}...}

It will certainly be executed to drawChild (canvas, child, drawingTime); in the name of this function, it is to draw a child control.

Protected boolean drawChild (Canvas canvas, View child, long drawingTime) {boolean more = false; final int cl = child. mLeft; final int ct = child. mTop; final int cr = child. mRight; final int cb = child. mBottom; final int flags = mGroupFlags; Transformation transformToApply = null; // obtain the Animation final Animation a = child bound to the View. getAnimation (); boolean concatMatrix = false; // if the View has an animation, it enters the if judgment and execution. if there is no animation, only the painting is performed. Control. If (! = Null) {if (mInvalidateRegion = null) {mInvalidateRegion = new RectF ();} final RectF region = mInvalidateRegion; final boolean initialized = a. isInitialized (); if (! Initialized) {// call the initialization function of Animation. The parameters of Animation are parsed. Convert values of different xy types. A. initialize (cr-cl, cb-ct, getWidth (), getHeight ();. initializeInvalidateRegion (0, 0, cr-cl, cb-ct); child. onAnimationStart () ;}if (mChildTransformation = null) {mChildTransformation = new Transformation () ;}// get the transform (translation, rotation, or scaling) information, the drawingTime that is passed in represents the drawn time in milliseconds. The obtained result is put into mChildTransformation. // MChildTransformation is a class of graphic conversion information, including a Matrix and alpha values. Matrix is the graphic conversion Matrix. // More is the return value of the function. It is easy to analyze the Code. If the animation does not end, only true is returned. If the animation ends, false is returned. More =. getTransformation (drawingTime, mChildTransformation); transformToApply = mChildTransformation; // returns true concatMatrix = a by default. willChangeTransformationMatrix (); // more = true to enter the loop if (more) {// more = true when the animation is not over if (! A. willChangeBounds () {if (flags & (Volumes | FLAG_ANIMATION_DONE) = tags) {mGroupFlags | = tags;} else if (flags & FLAG_INVALIDATE_REQUIRED) = 0) {mPrivateFlags | = DRAW_ANIMATION; // The animation will not stop calling the invalidate function to re-paint the animation view. invalidate (cl, ct, cr, cb);} else {. getInvalidateRegion (0, 0, cr-cl, cb-ct, region, transformToApply); mPriva TeFlags | = DRAW_ANIMATION; final int left = cl + (int) region. left; final int top = ct + (int) region. top; // If the animation is not over, the invalidate function will be repeatedly called to repaint the animation view invalidate (left, top, left + (int) region. width (), top + (int) region. height () ;}} else if (flags & FLAG_SUPPORT_STATIC_TRANSFORMATIONS) = FLAG_SUPPORT_STATIC_TRANSFORMATIONS) {if (mChildTransformation = null) {mChildTransformation = new Transform Ation ();} final boolean hasTransform = convert (child, mChildTransformation); if (hasTransform) {final int transformType = mChildTransformation. getTransformationType (); transformToApply = transformType! = Transformation. TYPE_IDENTITY? MChildTransformation: null; concatMatrix = (transformType & Transformation. TYPE_MATRIX )! = 0 ;}}... child. computeScroll (); // In simple analysis, the view is within the visible range. sx and sy should be equal to 0 ?? Final int sx = child. mScrollX; final int sy = child. mScrollY ;... final boolean hasNoCache = cache = null; final int restoreTo = canvas. save (); if (hasNoCache) {canvas. translate (cl-sx, ct-sy);} else {// here translate the canvas's zuobiao ???? @ Auth: qhyuan // translate the canvas to the (cl, ct) point. The cl and ct are the distance from the upper left corner of childView to the (0, 0) point on the screen. This is very important. When re-painting an animation, the left side of the canvas changes !!! It is not always at the (0, 0) Point of the screen. // The coordinate system after translation is different from the original one. Generally, the coordinate origin is moved to the upper left corner of the View. Canvas. translate (cl, ct); if (scalingRequired) {// mAttachInfo cannot be null, otherwise scalingRequired = false final float scale = 1.0f/mAttachInfo. mApplicationScale; canvas. scale (scale, scale) ;}} float alpha = 1.0f; if (transformToApply! = Null) {if (concatMatrix) {int transX = 0; int transY = 0; if (hasNoCache) {transX =-sx; transY =-sy ;} // The two parameters are 0 canvas. translate (-transX,-transY); // transformToApply is the conversion information class obtained from Animation to obtain the transformation matrix. This transformation matrix is different at different times, because the drawingTime passed in is different. // Transform the matrix of the canvas to achieve the animation effect. Canvas. concat (transformToApply. getMatrix (); canvas. translate (transX, transY); mGroupFlags | = FLAG_CLEAR_TRANSFORMATION ;}... if (alpha <1.0f & hasNoCache) {final int multipliedAlpha = (int) (255 * alpha); if (! Child. onSetAlpha (multipliedAlpha) {canvas. saveLayerAlpha (sx, sy, sx + cr-cl, sy + cb-ct, multipliedAlpha, Canvas. HAS_ALPHA_LAYER_SAVE_FLAG | Canvas. CLIP_TO_LAYER_SAVE_FLAG);} else {child. mPrivateFlags | = ALPHA_SET ;}} else if (child. mPrivateFlags & ALPHA_SET) = ALPHA_SET) {child. onSetAlpha (255 );}... return more ;}

Next let's take a look at the Animation class: There are two important functions in the Animation class: protected void applyTransformation (float interpolatedTime, Transformation t)

And public boolean getTransformation (long currentTime, TransformationoutTransformation );

The Transformation class contains a Transformation matrix and alpha value.

ApplyTransformation function: input a difference time to fill in a Transformation class. It is called in the getTransformation function. The applyTransformation of the Animation class is an empty implementation. The specific XXXAnimation function is implemented when it inherits from the Animation.

GetTransformation function: called in the drawChild function.

Public boolean getTransformation (long currentTime, Transformation outTransformation) {// currentTime will be passed through getDrawTime in the drawChild function if (mStartTime =-1) {mStartTime = currentTime ;} final long startOffset = getStartOffset (); final long duration = mDuration; float normalizedTime; if (duration! = 0) {// normalization time, so that normalizedTime is between 0 and 1. NormalizedTime = (float) (currentTime-(mStartTime + startOffset)/(float) duration ;} else {// time is a step-change with a zero duration normalizedTime = currentTime <mStartTime? 0.0f: 1.0f;} // expired indicates "Expiration". If the normalization time is greater than 1, expired = true, that is, expire indicates that the animation ends with final boolean expired = normalizedTime> = 1.0f; mMore =! Expired; if (! MFillEnabled) normalizedTime = Math. max (Math. min (normalizedTime, 1.0f), 0.0f); if (normalizedTime >=0.0f | mFillBefore) & (normalizedTime <= 1.0f | mFillAfter) {if (! MStarted) {if (mListener! = Null) {// record start animation mListener. onAnimationStart (this);} mStarted = true;} if (mFillEnabled) normalizedTime = Math. max (Math. min (normalizedTime, 1.0f), 0.0f); if (mCycleFlip) {normalizedTime = 1.0f-normalizedTime;} // obtain the interpolation time through the normalized time, similar to a function f (t) obtain the interpolation time based on the normalized time. // The function of interpolation time is to get the effect of changing the speed. For example, linear interpolation is f (t) = t final float interpolatedTime = mInterpolator. getInterpolation (normalizedTime); // call the applyTransformation function. The specific implementation is implemented in the class inherited from Animation. // In short, the interpolation time is passed in, then, this function fills in a specific conversion matrix based on the interpolation time. different time points correspond to different conversion matrices. Through this conversion matrix, images in different locations are drawn. ApplyTransformation (interpolatedTime, outTransformation);} // if the animation ends, execute the following if (expired) {if (mRepeatCount = mRepeated) {if (! MEnded) {mEnded = true; if (mListener! = Null) {mListener. onAnimationEnd (this) ;}} else {if (mRepeatCount> 0) {mRepeated ++;} if (mRepeatMode = REVERSE) {mCycleFlip =! MCycleFlip;} mStartTime =-1; mMore = true; if (mListener! = Null) {mListener. onAnimationRepeat (this) ;}}} if (! MMore & mOneMoreTime) {mOneMoreTime = false; return true;} // you can find that true is always returned through analysis, unless the animation ends. // Always return true until the expired = true (the animation is over) @ auth: qhyuan return mMore ;}

The applyTransformation function is empty by default in Animation and needs to be implemented in the subclass. That is to say, the Custom Animation must implement the applyTransformation function.

The interpolation class is also very simple. It is an interface with only one function.

public interface Interpolator {   float getInterpolation(float input);}

Common subclasses include:

The AccelerateDecelerateInterpolator speed changes slowly between the animation start and end, accelerating

AccelerateInterpolator changes slowly at the beginning of the animation, and then starts acceleration.

AnticipateInterpolator starts and then throws backward.

AnticipateOvershootInterpolator returns the final value after a certain value is pushed forward.

When the BounceInterpolator animation ends

CycleInterpolator: specifies the number of times the animation is played cyclically, and the speed changes along the sine curve.

DecelerateInterpolator is fast and slow at the beginning of the animation

LinearInterpolator changes at constant rate

OvershootInterpolator throws a value forward and returns to its original position.

You can also customize the interpolator. You only need to implement the getInterpolation function.


The previous section briefly introduced some functions involved in animation rendering. Next, we will take the execution of the animation as an example:

1. After XXXAnimation is defined, execute startAnimation of the View.

Public void startAnimation (Animation animation) {animation. setStartTime (Animation. START_ON_FIRST_FRAME); // set the Animation domain setAnimation (animation); // request to redraw the view invalidate ();}

The setAnimation function is as follows:

Public void setAnimation (Animation animation) {// set animation to the mCurrentAnimation attribute mCurrentAnimation = animation; if (animation! = Null) {animation. reset ();}}

2. then, the request for redrawing will execute the onDraw function and the dispatchDraw function and drawChild (canvas, child, drawingTime ); the drawingTime function is the time in milliseconds for this painting. The drawChild function has previously been explained.

3. first obtain the View's associated Animation, and then call the initialization function of Animation. In this case, the parameters of Animation are parsed. To convert values of different xy types, the initialize function is also implemented in the subclass of Animation. The initialize function in TranslateAnimation is as follows. In this function, the resolveSize function is called to parse the values in the constructor.

public void initialize(int width, int height, int parentWidth, int parentHeight) {        super.initialize(width, height, parentWidth, parentHeight);        mFromXDelta = resolveSize(mFromXType, mFromXValue, width, parentWidth);        mToXDelta = resolveSize(mToXType, mToXValue, width, parentWidth);        mFromYDelta = resolveSize(mFromYType, mFromYValue, height, parentHeight);        mToYDelta = resolveSize(mToYType, mToYValue, height, parentHeight);    }

The resolveSize function is as follows. The parsed values are parsed based on the absolute coordinates, relative controls, parent controls, and specific values. It is easy to see the absolute coordinates and the corresponding values are directly returned, the relative self and relative parent control multiply the value between 0 and 1 of the relative balance by the Child control or the value of the parent view width.

protected float resolveSize(int type, float value, int size, int parentSize) {        switch (type) {            case ABSOLUTE:                return value;            case RELATIVE_TO_SELF:                return size * value;            case RELATIVE_TO_PARENT:                return parentSize * value;            default:                return value;        }}

4. Then, the corresponding conversion matrix is obtained through the getTransformation function based on the re-painting time millisecond value. In this function, interpolatedTime = mInterpolator. getInterpolation (normalizedTime) is called first; interpolation time is obtained based on time.

Call the applyTransformation function. The function of TranslateAnimation is as follows:

Protected void applyTransformation (float interpolatedTime, Transformation t) {float dx = mFromXDelta; float dy = mFromYDelta; // The start X coordinate value is different from the end value if (mFromXDelta! = MToXDelta) {// dx corresponding to a certain time point (interpolation time). The linear interpolation interpolatedTime and normalizedTime are the same. Dx = mFromXDelta + (mToXDelta-mFromXDelta) * interpolatedTime);} if (mFromYDelta! = MToYDelta) {dy = mFromYDelta + (mToYDelta-mFromYDelta) * interpolatedTime);} // reset the conversion matrix and set the displacement difference to the conversion matrix. Translation is actually the multiplication of the transformation matrix and the original coordinate point. T. getMatrix (). setTranslate (dx, dy );}

As long as the animation is not completed, the getTransformation function will always return true. Then, if the return value is true, the invalidate function will be called again. The next step is to repeat steps 2, 3, and 4, but the drawing time is different during repeated execution, so the obtained conversion matrix is different, the positions of the new view are different. If false is returned, the animation is completed and the view is not repainted. The canvas is called when the view is drawn. translate (cl-sx, ct-sy); simply put, it is to move the coordinate system of the canvas from the upper left corner of the screen to the upper left corner of the animation view. Then, canvas. concat (transformToApply. getMatrix () is used for each animation. The matrix conversion operation is not performed for the dialog, And the animation is converted to the drawing.

The whole process is a little complicated. I haven't quite understood some functions yet, but the general idea is like this.


Let's look back at the several questions we raised:

1. When executing an animation, it is not the control itself changing, but its parent View. StartAnimation (anim) actually sets an animation for this View, rather than actual animation painting. The location is not changed at all, but it is determined by the location specified by layout.

2. The parameter meaning has been explained in the comments in the code.

3. There is a difference between fromDegrees and toDegrees in fromXValue and toXValue rotation animations. The specifics depend on how the conversion matrix is generated in the code. For example:

dx = mFromXDelta + ((mToXDelta - mFromXDelta) *interpolatedTime);

The interpolation time is changed from 0 to 1. Assume that the control width is 100 relative to its own type, and the values of mFromXDelta are parsed to actual values (from 0 ~ 1.0 this phase and its relative parent View become pixel width and height)

The effect of changing from 0 to 1.0f is: translate 100 from the current position to the right, because the dx of the first frame is 0, and dx of the last frame is 100.

The effect of changing from-1.0f to 0 is: pan to the current position from the left 100 of the current position, because the dx of the first frame is-100, and dx of the last frame is 0.

In a rotation animation, it is similar to clockwise and counterclockwise rotation by specifying the plus or minus sign of the start and end angle. You can understand the differences between the four animations in the lower part of the timeline. Let's take a look at the influence of positive and negative start and end values on rotation.

rotateAnimation= new RotateAnimation(0, 90, Animation.RELATIVE_TO_PARENT, -0.5f, Animation.RELATIVE_TO_PARENT,0);rotateAnimation= new RotateAnimation(90, 0, Animation.RELATIVE_TO_PARENT, -0.5f,Animation.RELATIVE_TO_PARENT, 0);rotateAnimation= new RotateAnimation(-90, 0, Animation.RELATIVE_TO_PARENT, -0.5f,Animation.RELATIVE_TO_PARENT, 0);rotateAnimation = new RotateAnimation(0, -90,Animation.RELATIVE_TO_PARENT, -0.5f, Animation.RELATIVE_TO_PARENT, 0);

4. Questions about four parameters of RotateAnimation: int release txtype, float release txvalue, int release tytype, and float release tyvalue:

As we mentioned earlier, the canvas is moved to the upper-left corner of the View during the animation, so the reference coordinate origin of the rotation animation is always the upper-left corner of the View. The rotation point can be specified arbitrarily. The reference coordinate of the rotation point should be the upper left corner of the View.

There is a dot in the center of the mobile phone screen. Now we want to rotate 360 degrees in the center of the midpoint on the far left side of the screen. The Code should be:

New RotateAnimation (0,360, Animation. RELATIVE_TO_PARENT,-0.5f, Animation. RELATIVE_TO_PARENT, 0); // note that it is-0.5f

Rotate in the upper left corner of the screen:

New RotateAnimation (0,360, Animation. RELATIVE_TO_PARENT,-0.5f, Animation. RELATIVE_TO_PARENT,-0.5f); // note that it is negative.

Rotate in center:

new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f,Animation.RELATIVE_TO_SELF, 0.5f);

Rotate at the bottom of the current vertex:

new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f,Animation.RELATIVE_TO_SELF, 1.0f);

Other rotation points and so on. I used to read these parameters, so when I write code that rotates around the upper left corner of the screen, I take it for granted:

New RotateAnimation (0,360, Animation. RELATIVE_TO_PARENT, 0, Animation. RELATIVE_TO_PARENT, 0 );

I originally thought that (0, 0) is the point in the upper left corner of the screen relative to the parent View. Of course, the results are incorrect. We can see that the rotation point at this time is still the upper left corner of the View. Is there no relationship with RELATIVE_TO_PARENT?

To sum up, the following four parameters can only calculate the distance from the canvas coordinate, but do not forget the source of the canvas if it is the value of the parent View.


During the analysis, I only roughly studied the animation rendering process. The actual code is very complicated. If the explanation is incorrect, contact me.


Demo of parameters for testing animation translation and rotation: Click here to download












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.