Learn more about some Android animations

Time:2021-2-26

1、 Propertyvaluesholder

Reading this article requires the basis of Android attribute animation in the previous article, so that you can understand what to talk about next.

1. Understand and use

PropertyValuesHolderIt’s a method similar to objectanimation, but it’s missing a target, which is the control to be executed. Look at the normal usage: all holders will be executed at the same time

public void  doPropertyValuesHolder(){
        //Define a rotation holder
        PropertyValuesHolder rotationHolder=
                PropertyValuesHolder.ofFloat(
                        "rotation",
                        60f,40f,100f,-60f,40f,88f,77f);

        //Define a transparent holder
        PropertyValuesHolder alphaHolder=
                PropertyValuesHolder.ofFloat(
                        "alpha",
                        0.01f,0.5f,1.0f,0.8f,0.2f,0.0f);
        
    	//Load into objectanimator
        ObjectAnimator objectAnimator=ObjectAnimator.ofPropertyValuesHolder(ballImageView,rotationHolder,alphaHolder);
        objectAnimator.setDuration(3000);
        objectAnimator.start();
    }

2. Methods and parameters

You can see the parameters of this method:

ObjectAnimator ofPropertyValuesHolder(Object target,PropertyValuesHolder... values)
PropertyValuesHolder ofFloat(String propertyName, float... values)

Object targetIs the control to display the animation

PropertyValuesHolder... valuesLoad multiplePropertyValuesHolder

String propertyNameRepresents the parameter to be reflected, which is the same as the parameter of objectanimation

float... valuesRepresents the variable length parameter
There are also the following pictures:

图片.png

amongofObject()Method, similar to objectanimation, is to customize typeevaluator.
图片.png

2、 Keyframe

1. Understand and use

Look at the name, is understood as the meaning of the key frame, in the animation, do some operations in a frame, so as to achieve a more obvious contrast effect.
Keyframes represent where an object should be at which time point.
Specific use:

public void  doPropertyValuesHolderKeyFrame(){

        //The first keyframe1 starts from 0.6. When the progress is 60%, the value is 0.1f
        Keyframe keyframe1=Keyframe.ofFloat(0.6f,0.1f);

        //Middle keyframe2
        Keyframe keyframe2=Keyframe.ofFloat(0.1f,0.8f);

        //The tail keyframe3 ends with 50% progress, and the value is 0.2f
        Keyframe keyframe3=Keyframe.ofFloat(0.5f,0.2f);

        //Load it into the holder, and set the method to be reflected, which is the setalpha () method to control the transparency
        PropertyValuesHolder alphaHolder=PropertyValuesHolder.ofKeyframe("alpha",keyframe1,keyframe2,keyframe3);

        //Load to holder, load to objectanimator or valueanimation
        ObjectAnimator objectAnimator=ObjectAnimator.ofPropertyValuesHolder(ballImageView,alphaHolder);
        objectAnimator.setDuration(3000);
        objectAnimator.start();
    }

2. Methods and parameters

Keyframe ofFloat(float fraction, float value)

float fractionIndicates progress

float valueRepresents the value under this schedule

PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values)

String propertyName Set method to reflect

Keyframe... valuesPass in keyframe

Keyframe’s method is similar to others.
图片.png

Keyframe set method, set progress, interpolator, numerical value.
When no interpolator is set, the default is linear interpolator

Keyframe1. Setinterpolator (new linear interpolator()); // default linear interpolator

图片.png

3. Frame operation

Write the conclusion directly

  • If 0 frame is removed, the first key frame is taken as the starting position
  • If the end frame is removed (the progress is 1), the last key frame will be taken as the end position
  • You need at least 2 frames to build an animation using keyframe

3、 Viewpropertyanimator

1. Understand and use

You can quickly define the animation in the form of serial, save some definitions, and start the animation every time you draw the interface, which saves more energy than others.
For example:

ballImageView.animate().alpha(0.5f).rotation(360f).scaleX(1.5f).translationX(100f);

2. Parameters and methods

You can see that the return values of these methods are basicallyViewPropertyAnimator
图片.png
图片.png
Quote another table:

function meaning
alpha(float value) Set transparency
scaleY(float value) Sets the scale size in the Y direction
scaleX(float value) Sets the x-axis scaling size
translationY(float value) Set the movement value in the y-axis direction
translationX(float value) Set the movement value in the x-axis direction
rotation(float value) Sets the degree of rotation around the Z axis
rotationX(float value) Sets the degree of rotation about the X axis
rotationY(float value) Sets the degree of rotation about the Y axis
x(float value) The final position of the upper left corner coordinate relative to the parent container in the x-axis direction
y(float value) The final position of the upper left corner coordinate relative to the parent container in the y-axis direction
alphaBy(float value) Set transparency increment
rotationBy(float value) Sets the rotation increment around the Z axis
rotationXBy(float value) Set the rotation increment around x oil
rotationYBy(float value) Sets the y-axis rotation increment
translationXBy(float value) Sets the increment of the movement value in the x-axis direction
translationYBy(float value) Sets the increment of the movement value in the y-axis direction
scaleXBy(float value) Sets the x-axis scaling increment
scaleYBy(float value) Sets the scaling increment in the Y direction
xBy(float value) The position increment of the upper left corner coordinate relative to the parent container in the x-axis direction
yBy(float value) The position increment of the upper left corner coordinate relative to the parent container in the y-axis direction
setlnterpolator(Timelnterpolator interpolator) Set interpolator
setStartDelay(long startDelay) Set start delay
setDuration(long duration) Set animation duration

4、 Animatelayoutchanges

 android:animateLayoutChanges="true"
When adding or removing controls in layout, add animation, but only use the default animation.

5、 Layouttransition

Layouttransition can control the animation of ViewGroup, and you can use custom animation.
Specific use:

public void doLayoutTransition(){

        LinearLayout linearLayout=new LinearLayout(this);

        //1. Create instance
        LayoutTransition transition=new LayoutTransition();

        //2. Create animation
        ObjectAnimator objectAnimator=ObjectAnimator.ofFloat(null,"rotation",0f,90f,0f);

        //3. Set the animation form
        transition.setAnimator(LayoutTransition.DISAPPEARING,objectAnimator);

        //4. Set the layouttransition to ViewGroup
        linearLayout.setLayoutTransition(transition);
     
      	//5. Nineold androids, an open source animation library
     
    }

setAnimator(int transitionType, Animator animator)

In this method,transitionTypeThere are five options

image.png

CHANGE_APPEARINGBecause a new element needs to be displayed in the container, the animation applied to other elements that need to be changed (many problems, not commonly used)

_CHANGE_DISAPPEARING_When an element in a container is about to disappear, the animation applied to other elements that need to be changed (many problems, not commonly used)

_CHANGING_Animation changes of the changing element in the container

_APPEARING_Animation defined when an element appears in a container

_DISAPPEARING_Animation defined when an element disappears in a container

6、 Pathmeasure

PathMeasureSimilar to a calculator, can calculate the target path coordinates, length and so on

1. Initialization

public void doPathMeasure(){
        Path path=new Path();

        //Initialization method 1
        PathMeasure pathMeasure1=new PathMeasure();
        pathMeasure1.setPath(path,true);

        //Initialization method 2
        PathMeasure pathMeasure2=new PathMeasure(path,false);
    }

setPath(Path path, boolean forceClosed)
pathIt represents the target path to be calculated.
forceClosedWhether the path is closed or not, true will calculate the path in the closed state, and false will calculate the path according to the original situation.

2. Function call

Customize a view

public class PathView extends View {
    Path mPath;
    Paint mPaint;
    PathMeasure mPathMeasure;

    public PathView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        mPath=new Path();
        mPaint=new Paint();
        mPathMeasure=new PathMeasure();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        canvas.translate (250250); // Canvas move
        mPaint.setColor ( Color.BLUE ); // brush color
        mPaint.setStrokeWidth (5) ; // brush thickness
        mPaint.setStyle ( Paint.Style.STROKE ); // brush style

        mPath.moveTo(0,0);
        mPath.lineTo(0,100);
        mPath.lineTo(100,100);
        mPath.lineTo(100,0);

        mPathMeasure.setPath(mPath,true);
        Log.v("showLog",
                "getLength()=="+mPathMeasure.getLength()
                        +"  isClosed()=="+  mPathMeasure.isClosed ()); // result 400.0 true

        mPathMeasure.setPath(mPath,false);
        Log.v("showLog",
                "getLength()=="+mPathMeasure.getLength()
                        +"  isClosed()=="+  mPathMeasure.isClosed ()); // result 300.0 false

        canvas.drawPath (mpath, mpaint); // draw path
    }
}

Rendering effect:

image.png

2.1 PathMeasure.getLength()

PathMeasure.getLength()Function is used to measure the length of a path

2.2 PathMeasure.isClosed()

PathMeasure.isClosed()Function to return whether the closed state is measured

2.3 PathMeasure.nextContour()

mPath.addRect(-50, -50, 50, 50, Path.Direction.CW);
        canvas.drawPath(mPath, mPaint);

        mPath.addRect(-100, -100, 100, 100, Path.Direction.CW);
        canvas.drawPath(mPath, mPaint);

        mPath.addRect(-120, -120, 120, 120, Path.Direction.CW);
        canvas.drawPath(mPath, mPaint);

        mPathMeasure.setPath(mPath, false);

        do {
            float len = mPathMeasure.getLength();
            Log.v("showLog", "len=" + len);
        } while (mPathMeasure.nextContour());

effect:

image.png

Print results:

len=400.0
len=800.0
len=960.0

PathMeasure.nextContour()The order obtained is the same as that of the added paths

PathMeasure.getLength()Just get the length of the current path, not the whole length

2.3 getSegment()

To use the getsegment function, you need to disable hardware acceleration and add it to the constructor
setLayerType(LAYER_TYPE_SOFTWARE,null);

mPath.addRect(-50, -50, 50, 50, Path.Direction.CW);
        mPathMeasure.setPath (mpath, false); // calculated path
        mPathMeasure.getSegment (0150, mdstpath, true); // intercepts and adds to mdstpath, which is adding, not other
        canvas.drawPath (mpath, mpaint); // draw the original path

        canvas.translate (200,0); // Canvas move
        mPaint.setColor(Color.RED);
        canvas.drawPath (mdstpath, mpaint); // draw the added mdstpath
boolean getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo)

startD Path the starting point of interception. The starting point of interception is the point in the upper left corner

stopD Intercept stop point

dst Path added to after interception

startWithMoveTo  Whether to save the original state, true will save the original state, and false will connect the initial point and the end point, which may not be the same shape as the original one
The effect of the above code: the direction of the screenshot is related to the generation direction of the original path

image.png

2.4 examples of dynamic circle drawing

code:

public class PathView extends View {
    Path mPath, mDstPath;
    Paint mPaint;
    PathMeasure mPathMeasure;
    float mCurAnimValue;

    public PathView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        setLayerType(LAYER_TYPE_SOFTWARE, null);
        mPath = new Path();
        mDstPath = new Path();
        mPaint = new Paint();
        mPathMeasure = new PathMeasure();

        mPaint.setColor ( Color.BLUE ); // brush color
        mPaint.setStrokeWidth (5) ; // brush thickness
        mPaint.setStyle ( Paint.Style.STROKE ); // brush style

        mPath.addCircle (100, 100, 50,  Path.Direction.CW ); // a complete circle
        mPathMeasure.setPath (mpath, true); // path to calculate

        ValueAnimator animator =  ValueAnimator.ofFloat (0, 1); // progress 0 ~ 1
        animator.setRepeatCount ( ValueAnimator.INFINITE ); // infinite loop
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mCurAnimValue = (Float)  animation.getAnimatedValue (); // get the current progress
                Invalidate(); // redraw, rerun ondraw() method
            }
        });
        animator.setDuration(5000);
        animator.start();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.translate (100, 100); // Canvas move

        float stop= mPathMeasure.getLength () * mcuranimvalue; // one intercept point is determined by one progress
        mDstPath.reset();
        mPathMeasure.getSegment (0, stop, mdstpath, true); // add a little bit

        canvas.drawPath (mdstpath, mpaint); // every time there is a progress update, a small section of interception is drawn
    }
}

effect:
动态画圆.gif

2.5 getPosTan()

Let’s first look at the definition of the function

boolean getPosTan(float distance, float pos[], float tan[])

float distanceThe actual length of the distance path

float pos[]The coordinate value of the point. X and Y POS [0] = x, POS [1] = y

float tan[]The tangent value of the point. X and Y POS [0] = x, POS [1] = y Tan

2.6 examples of arrow circles

code:

public class PathView extends View {
    Path mPath, mDstPath;
    Paint mPaint;
    PathMeasure mPathMeasure;
    float mCurAnimValue;
    Bitmap mArrowBmp;
    float[] mPos;
    float[] mTan;
    int mCenterX,mCenterY;
    float mRadius;

    public PathView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        setLayerType(LAYER_TYPE_SOFTWARE, null);
        mPath = new Path();
        mDstPath = new Path();
        mPaint = new Paint();
        mPathMeasure = new PathMeasure();
        mPos=new float[2];
        mTan=new float[2];

        //Load arrow image
        mArrowBmp= BitmapFactory.decodeResource(getResources(), R.drawable.arrow);

        mPaint.setColor ( Color.BLUE ); // brush color
        mPaint.setStrokeWidth (5) ; // brush thickness
        mPaint.setStyle ( Paint.Style.STROKE ); // brush style

        mPath.addCircle (540, 972, 486,  Path.Direction.CW ); // a complete circle
        mPathMeasure.setPath (mpath, true); // path to calculate

        ValueAnimator animator =  ValueAnimator.ofFloat (0, 1); // progress 0 ~ 1
        animator.setRepeatCount ( ValueAnimator.INFINITE ); // infinite loop
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mCurAnimValue = (Float)  animation.getAnimatedValue (); // get the current progress
                Invalidate(); // redraw, rerun ondraw() method
            }
        });
        animator.setDuration(5000);
        animator.start();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {

        /*
         *Get the smallest value of H, W;
         *The shift of > > 1 is the same as that of / 2;
         *Multiply by 0.9f to represent 90% of the layout
         * */
        mRadius = (Math.min(h, w) >> 1) * 0.9f;

        //Central coordinates
        mCenterX = w / 2;
        mCenterY = h / 2;
        Log.v("showLog",mCenterX+"  "+mCenterY+"  "+mRadius);
        postInvalidate();
        super.onSizeChanged(w, h, oldw, oldh);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        float stop= mPathMeasure.getLength () * mcuranimvalue; // one intercept point is determined by one progress

        mDstPath.reset();
        mPathMeasure.getSegment (0, stop, mdstpath, true); // add a little bit

        canvas.drawPath (mdstpath, mpaint); // every time there is a progress update, a small section of interception is drawn

        mPathMeasure.getPosTan (stop, MPOs, mtan); // obtain the tangent value and coordinates of each point

        /**
         *     Math.atan2 (mtan [1], mtan [0]) to obtain the radian of tan
         *    *180.0/ Math.PI Convert to angle value
         * */
        float degrees=(float)(Math.atan2(mTan[1],mTan[0])*180.0/Math.PI);

        Matrix matrix=new Matrix();

        /**
         *Rotates the picture by a specified angle around the center point
         * postRotate(float degrees, float px, float py)
         *Degrees is the angle (PX, py) is the center of the picture
         * */
        matrix.postRotate(degrees,mArrowBmp.getWidth()/2,mArrowBmp.getHeight()/2);

        /**
         *Move the picture from the default (0, 0) point to the front of the path
         * */
        matrix.postTranslate(mPos[0]-mArrowBmp.getWidth()/2,mPos[1]-mArrowBmp.getHeight()/2);

        //Draw a picture
        canvas.drawBitmap(mArrowBmp,matrix,mPaint);

    }
}

effect:

箭头动态画圆.gif

2.7 getMatrix()

Parameter type:

boolean getMatrix(float distance, Matrix matrix, int flags)

usage method:

//Calculate azimuth
        Matrix matrix = new Matrix();

		//Get location information
        mPathMeasure.getMatrix(stop,matrix,PathMeasure.POSITION_MATRIX_FLAG);

		//Get trimming information
        mPathMeasure.getMatrix(stop,matrix,PathMeasure.TANGENT_MATRIX_FLAG);

2.8 successful payment examples

public class TickView extends View {
    Path mPath, mDstPath;
    Paint mPaint;
    PathMeasure mPathMeasure;
    float mCurAnimValue;
    int mCenterX, mCenterY;
    float mRadius;

    public TickView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        setLayerType(LAYER_TYPE_SOFTWARE, null);
        mPath = new Path();
        mDstPath = new Path();
        mPaint = new Paint();
        mPathMeasure = new PathMeasure();

        mPaint.setColor ( Color.BLUE ); // brush color
        mPaint.setStrokeWidth (5) ; // brush thickness
        mPaint.setStyle ( Paint.Style.STROKE ); // brush style

        mCenterX = 540;
        mCenterY = 972;
        mRadius = 486 / 2;

        /**
         *Circle
         * */
        mPath.addCircle(mCenterX, mCenterY, mRadius, Path.Direction.CW);

        /**
         *Check
         * */
        mPath.moveTo(mCenterX - mRadius / 2, mCenterY);
        mPath.lineTo(mCenterX, mCenterY + mRadius / 2);
        mPath.lineTo(mCenterX + mRadius / 2, mCenterY - mRadius / 3);

        mPathMeasure.setPath (mpath, false); // the path to be calculated

        ValueAnimator animator =  ValueAnimator.ofFloat (0, 2); // progress 0 ~ 1 is circle and 1 ~ 2 is check mark
        animator.setRepeatCount(ValueAnimator.RESTART);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mCurAnimValue = (Float)  animation.getAnimatedValue (); // get the current progress
                Invalidate(); // redraw, rerun ondraw() method
            }
        });
        animator.setDuration(5000);
        animator.start();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {

        /*
         *Get the smallest value of H, W;
         *The shift of > > 1 is the same as that of / 2;
         *Multiply by 0.9f to represent 90% of the layout
         * */
        mRadius = (Math.min(h, w) >> 1) * 0.9f;

        //Central coordinates
        mCenterX = w / 2;
        mCenterY = h / 2;
        Log.v("showLog", mCenterX + "  " + mCenterY + "  " + mRadius);
        postInvalidate();
        super.onSizeChanged(w, h, oldw, oldh);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (mCurAnimValue < 1) {
            float stop = mPathMeasure.getLength() * mCurAnimValue;
            mPathMeasure.getSegment(0, stop, mDstPath, true);
        } else if (mCurAnimValue == 1) {
            mPathMeasure.getSegment(0, mPathMeasure.getLength(), mDstPath, true);
            mPathMeasure.nextContour();
        } else {
            float stop = mPathMeasure.getLength() * (mCurAnimValue - 1);
            mPathMeasure.getSegment(0, stop, mDstPath, true);
        }

        canvas.drawPath(mDstPath, mPaint);
    }
}

effect:
对勾动画.gif

How many setbacks will we encounter in programming? The end of desert must be oasis.