Reading this article requires the foundation of Android attribute animation in the previous article, so that you can understand what to say next.
1. Understanding and use
PropertyValuesHolder is a method similar to ObjectAnimation, but with one target missing, it 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 target is the control to display the animation
PropertyValuesHolder... values loads multiple propertyvaluesholders
String propertyName represents the parameter to be reflected, which is the same as that of ObjectAnimation
float... values are variable length parameters
This method also has the following pictures:
The ofObject() method is similar to ObjectAnimation, and also needs to customize the TypeEvaluator.
1. Understanding and use
Look at the name, is to understand the meaning of key frame. In the animation, do some operations in a certain frame, so as to achieve a more obvious effect of contrast effect.
Keyframes represent where an object should be at which point in time.
Specific use:
public void doPropertyValuesHolderKeyFrame(){ //First keyframe1, starting from schedule 0.6, when the schedule 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); //Tail keyframe3, ending with 50% progress, the value is 0.2f at this time Keyframe keyframe3=Keyframe.ofFloat(0.5f,0.2f); //Load into the Holder and set the method to reflect. This is reflected by setAlpha() method, which controls transparency PropertyValuesHolder alphaHolder=PropertyValuesHolder.ofKeyframe("alpha",keyframe1,keyframe2,keyframe3); //Load into Holder 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 fraction represents progress
float value indicates the value under this progress
PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values)
String propertyName set method to reflect
Keyframe... values in keyframe
Keyframe's method is similar to other methods.
Keyframe set method, set progress, interpolator, numerical value.
When no interpolator is set, the default is linear interpolator
keyframe1.setInterpolator(new LinearInterpolator()); //Default linear interpolator
3. Frame operation
Write a conclusion directly:
- If frame 0 is removed, the first key is taken as the starting position
- If the end frame is removed (progress is 1), the end position is the last keyframe
- Using keyframe to build an animation requires at least 2 frames
1. Understanding and use
The animation can be defined quickly and some definitions can be omitted in the form of serial. When drawing the interface, the animation can be started, which saves more consumption than others.
For example:
ballImageView.animate().alpha(0.5f).rotation(360f).scaleX(1.5f).translationX(100f);
2. Parameters and methods
You can see the return values of these methods, which are basically ViewPropertyAnimator
Refer to another table:
android:animateLayoutChanges="true"
Add animation when Layout adds controls or removes controls, but only the default animation can be used.
<LinearLayout android:animateLayoutChanges="true" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"/>5, LayoutTransition
LayoutTransition can control the animation of ViewGroup, and can use customized animation.
Specific use:
public void doLayoutTransition(){ LinearLayout linearLayout=new LinearLayout(this); //1. Create an instance LayoutTransition transition=new LayoutTransition(); //2. Create animation ObjectAnimator objectAnimator=ObjectAnimator.ofFloat(null,"rotation",0f,90f,0f); //3. Set the form of animation transition.setAnimator(LayoutTransition.DISAPPEARING,objectAnimator); //4. Set LayoutTransition to ViewGroup linearLayout.setLayoutTransition(transition); //5. Open source animation library NineOldAndroids }
setAnimator(int transitionType, Animator animator)
In this method, the transitionType has five options
Change ABCD animating animation applied by other elements that need to change because a new element is to be displayed in the container
_Change ﹣ separating ﹣ when an element in a container is going to disappear, the animation applied by other elements that need to change (many problems, not commonly used)
_CHANGING? Animation changes for the element being changed in the container
_Animation defined when the applying UU element appears in the container
_Animation defined when the separating UU element disappears in the container
6, PathMeasurePathMeasure is similar to a calculator, which can calculate the coordinates and length of the target path
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)
Path is the target path to be calculated.
If forceClosed is closed, true will calculate the Path in the closed state, and false will calculate according to the original situation of Path.
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(250,250); //Canvas move mPaint.setColor(Color.BLUE); //stroke 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 } }
Painting effect:
2.1 PathMeasure.getLength()
The PathMeasure.getLength() function measures the length of the path
2.2 PathMeasure.isClosed()
The PathMeasure.isClosed() function returns whether to measure the closed state
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:
Print results:
len=400.0 len=800.0 len=960.0
PathMeasure.nextContour() gets in the same order as the Path you added
PathMeasure.getLength() just gets the length of the current path, not the whole length
2.3 getSegment()
Using the getSegment function requires disabling hardware acceleration to add 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(0,150,mDstPath,true); //Intercept and add to mDstPath, add, not others 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)
The starting point of the interception of startD path is the point in the upper left corner
stopD intercept stop point
path added after dst interception
startWithMoveTo whether to save as is, true to save as is, false to connect the initial point and the end point, not necessarily the same shape as the original
The effect of the above code: the direction of the screenshot is related to the generation direction of the original path
2.4 example 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); //stroke 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 cycle animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mCurAnimValue = (Float) animation.getAnimatedValue(); //Get current progress invalidate();//Redraw, execute onDraw() method again } }); 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; //A progress determines a intercept point mDstPath.reset(); mPathMeasure.getSegment(0,stop,mDstPath,true); //Add a little bit canvas.drawPath(mDstPath,mPaint); //Every time there is a progress update, draw a small segment to intercept } }
effect:
2.5 getPosTan()
Let's first look at the definition of the function:
boolean getPosTan(float distance, float pos[], float tan[])
float distance the actual length of the path
float pos [] coordinate value of the point. X and y pos[0]=x, pos[1]=y
float tan [] tangent value of the point. X and y pos[0]=x, pos[1]=y Tan < a = Y / X
2.6 example of arrow drawing circle
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 picture mArrowBmp= BitmapFactory.decodeResource(getResources(), R.drawable.arrow); mPaint.setColor(Color.BLUE); //stroke 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 cycle animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mCurAnimValue = (Float) animation.getAnimatedValue(); //Get current progress invalidate();//Redraw, execute onDraw() method again } }); animator.setDuration(5000); animator.start(); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { /* * The minimum value of h,w is obtained; * >> 1 The shift is the same as / 2; * Multiply by 0.9f to represent 90% of the layout * */ mRadius = (Math.min(h, w) >> 1) * 0.9f; // Central coordinate 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; //A progress determines a intercept point mDstPath.reset(); mPathMeasure.getSegment(0,stop,mDstPath,true); //Add a little bit canvas.drawPath(mDstPath,mPaint); //Every time there is a progress update, draw a small segment to intercept mPathMeasure.getPosTan(stop,mPos,mTan); //Obtain tangent value and coordinates of each point /** * Math.atan2(mTan[1],mTan[0])Get the radian value 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(); /** * Rotate the picture around the center point by a specified angle * 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 pictures canvas.drawBitmap(mArrowBmp,matrix,mPaint); } }
effect:
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 payment success 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); //stroke color mPaint.setStrokeWidth(5); //Brush thickness mPaint.setStyle(Paint.Style.STROKE); //Brush style mCenterX = 540; mCenterY = 972; mRadius = 486 / 2; /** * circular * */ mPath.addCircle(mCenterX, mCenterY, mRadius, Path.Direction.CW); /** * Check mark * */ mPath.moveTo(mCenterX - mRadius / 2, mCenterY); mPath.lineTo(mCenterX, mCenterY + mRadius / 2); mPath.lineTo(mCenterX + mRadius / 2, mCenterY - mRadius / 3); mPathMeasure.setPath(mPath, false); //path to calculate ValueAnimator animator = ValueAnimator.ofFloat(0, 2); //Progress 0-1 is circle, 1-2 is checkmark animator.setRepeatCount(ValueAnimator.RESTART); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mCurAnimValue = (Float) animation.getAnimatedValue(); //Get current progress invalidate();//Redraw, execute onDraw() method again } }); animator.setDuration(5000); animator.start(); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { /* * The minimum value of h,w is obtained; * >> 1 The shift is the same as / 2; * Multiply by 0.9f to represent 90% of the layout * */ mRadius = (Math.min(h, w) >> 1) * 0.9f; // Central coordinate 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:
How many setbacks will we encounter in programming? Table give up, the end of the desert must be oasis.