For more examples of custom views, see android Custom View Index
Let’s do a quick sketch, in case you go to the wrong place.
You can only record it in HD for three seconds at a time, whatever it is, if you understand what it means.
You can see it’s a little bit more complicated, actually a little bit more complicated than usual.
Seeing this kind of animation, first of all, we need to analyze rationally, not to be confused by the superficial phenomenon, in fact, there are two things:
A: attribute
2: animation
To be specific, one is the change of the color and shape of the object, and the other is the beating of the animation.
attribute
So how to change the shape and color of an object dynamically, we can see from the above picture, we use three shapes, square, triangle and circle, give three drawing functions:
Square: canvas. DrawPath (rectPath, paint);Copy the code
Triangle: Canvas. drawPath(trianglePath,paint);Copy the code
Canvas. DrawCircle (controlWidth /2,controlWidth /2,controlWidth /2,paint);Copy the code
The squares and triangles here are drawn in the way of Path. If you don’t know what Path is, let’s do it in order to reduce the length.
Ok, so now we can draw these three shapes as much as we want, but when should we draw what shape? We can write an enumerated class to store our shapes and then switch:
// A list of morphed concentrated shapes, namely square, triangle, circle
public enum ShapeKind {
RECT,TRIANGLE,CIRCLE
}
Copy the code
Enumeration class, write well, contains three graphics, now switch to implement the dynamic graphic shapes, we can use a variable to increasing, for example, I give a variable (I) the initial value is 1, but every time redrawing added, if to 100, then started to switch to another graphics, and I will reset to 100, Then go to the loop, so that you can constantly dynamically switch shapes, as for the color is not even easier, each shape for a different brush, not three colors.
But sometimes we find that our redrawing speed is too fast, so we can slow it down with a timer:
handler.postDelayed(new Runnable() {
@Override
public void run() {
Message message =new Message();
message.what =1;
handler.sendMessage(message);
}
},20);
Copy the code
This will be redrawn every 0.02 seconds, and the speed is under control.
Using this knowledge, we can create a control that changes shape and color, which we call TransformationLoading, and which is then used by the following animated class.
animation
Now let’s look at the motion of our object. We observed three motions, one up, one down, and one rotation, all at the same time, so I had to use a function that I don’t usually use:
AnimatorSetCopy the code
This function is used to aggregate individual animations.
ObjectAnimator objectAnimator =
ObjectAnimator.ofFloat(transformationLoading,"translationY",0,jumpDistance);
ObjectAnimator scaleIndication = ObjectAnimator.ofFloat(jumpShadow,"scaleX"And 0.2 f, 1 f); downAnimator =new AnimatorSet(); downAnimator.playTogether(objectAnimator, scaleIndication); downAnimator.setDuration(time);Copy the code
The first two lines above animate the shift and rotation respectively, the fourth line sets the two animations together, and the fifth line sets the animation time, so that’s the basic usage.
But take a closer look at the graph above, we find that there is a shadow underneath the bouncing object and three words in the load, don’t panic, see here we think we now this control is a container, so we inherit the LinearLayout, call it JumpLoading.
This includes three controls, one TransformationLoading, which we created in the properties section above, and a shadow, which is essentially an ImageView, plus a TextView.
Now that we know how to assemble the animation together, what about when to start and end the animation? Here are two functions:
// The interface is loaded. @override protected voidonFinishInflate() { super.onFinishInflate(); startAnimate(0); // The interface is destroyed @override protected voidonDetachedFromWindow() { super.onDetachedFromWindow(); stopAnimate(); // Stop animation}Copy the code
The two functions belong to the life cycle of the linearLayout, which is called back when it is just drawn and when it is destroyed, and here we can start and end the animation.
Speaking of all the operation process here we should be more clear, the specific operation we still want to go to the code to feel, the specific use of animation control we directly on the line, animation control code is used on the basis of the property code.
First give the code for the attribute:
/ * *
* Constantly changing graphics loading animation, imitation 58, reference link: github.com/zzz40500/an…
In general, the rate of color change is controlled by two values: colorChangeRate, time, rate of color change, and speed of color change
* /
public class TransformationLoadingextends View {
public static ShapeKindshapeKind =RECT;
private floatcurrentColorChange =0f; / / the current color switch gross private ArgbEvaluatormAnimPercent = new ArgbEvaluator (); Private Paintpaint; private Paintpaint; privatefloatcontrolWidth; // Control width private Handlerhandler; Private int mRectColor; private int mRectColor; Private int mTriangleColor; Private int mCircle; // The circle color is privatefloatColorChangeRate = 0.02 f; Private Long time =10; private long Time =10; // Animation execution speed. The recommended value is 10-100. The higher the value, the slower the animationfalse; Private Boolean FlashingOrNot =false; Private Boolean animateOrNot = private Boolean animateOrNot =true; Public TransformationLoading(Context Context) {super(Context); initData(); } public TransformationLoading(Context context, AttributeSet attrs) { super(context, attrs); initData(); } // Initialize the colors of several shapes private voidinitData() {mRectColor = color.argb (255,114,213,114); MTriangleColor = Color argb (255115153254); MCircle = Color argb (255232,78,64); paint =new Paint(); handler =newHandler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case1: invalidate(); // Redraw the interfacebreak; }}}; } @Override protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); controlWidth = getMeasuredWidth(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); switch (shapeKind) {caseRECT:// Draw a square currentColorChange +=colorChangeRate;if (FlashingOrNot){
int rectColor = (int)mAnimPercent.evaluate(currentColorChange,mRectColor,mTriangleColor);
paint.setColor(rectColor);
}else {
paint.setColor(mRectColor);
}
if (currentColorChange >1) {
if(changeKind) { shapeKind =TRIANGLE; } currentColorChange =0f; } Path rectPath =new Path(); RectPath. MoveTo (0, 0); rectPath.lineTo(controlWidth,0); rectPath.lineTo(controlWidth,controlWidth); rectPath.lineTo(0,controlWidth); rectPath.close(); canvas.drawPath(rectPath,paint);if (animateOrNot) {
toRefresh();
}
break;
caseTRIANGLE:// Draw a TRIANGLE currentColorChange +=colorChangeRate;if (FlashingOrNot){
int triangleColor = (int)mAnimPercent.evaluate(currentColorChange,mTriangleColor,mCircle);
paint.setColor(triangleColor);
}else {
paint.setColor(mTriangleColor);
}
if (currentColorChange >1) {
if(changeKind) { shapeKind =CIRCLE; } currentColorChange =0f; } Path trianglePath =new Path(); TrianglePath. MoveTo (0.5 f * controlWidth, 0). trianglePath.lineTo(controlWidth,controlWidth); trianglePath.lineTo(0,controlWidth); trianglePath.close(); canvas.drawPath(trianglePath,paint);if (animateOrNot) {
toRefresh();
}
break;
caseCurrentColorChange +=colorChangeRate;if (FlashingOrNot){
int circleColor = (int)mAnimPercent.evaluate(currentColorChange,mCircle,mRectColor);
paint.setColor(circleColor);
}else {
paint.setColor(mCircle);
}
if (currentColorChange >1) {
if (changeKind) {
shapeKind =RECT;
}
currentColorChange =0f;
}
canvas.drawCircle(controlWidth /2,controlWidth /2,controlWidth /2,paint);
if (animateOrNot) {
toRefresh();
}
break; }} // The method used to actively switch shapes public voidchangeShape() { currentColorChange =0f; // Reset the color gradient value changeKind =false; // Switch (shapeKind) {case RECT:
shapeKind =TRIANGLE;
break;
case TRIANGLE:
shapeKind =CIRCLE;
break;
case CIRCLE:
shapeKind =RECT;
break; }} // Refresh the page private voidtoRefresh() {
handler.postDelayed(new Runnable() {
@Override
public void run() { Message message =new Message(); message.what =1; handler.sendMessage(message); } },time); } public enum ShapeKind {TRIANGLE, TRIANGLE,CIRCLE} public voidstopAnimate() {
animateOrNot =false; } // Start animation public voidstartAnimate() {
if(! animateOrNot) { animateOrNot =true; toRefresh(); }}}Copy the code
Give the animation code again:
/ * *
* Imitation 58 loading animated bouncing balls, used in conjunction with the TransformationLoading control, and to turn off the active TransformationLoading controller, namely: changeKind
* /
public class JumpLoadingextends LinearLayout { private AnimatorSetupAnimator; Private AnimatorSetdownAnimator; // Drop animation set private Boolean move =false; Private Runnablerunnable; private TransformationLoadingtransformationLoading; Private ImageViewjumpShadow; Private int jumpDistance =200; private int jumpDistance =200; Private int time =500; Private int Angle =180; private int Angle =180; Public JumpLoading(Context Context) {super(Context); initData(context); } public JumpLoading(Context context, AttributeSet attrs) { super(context, attrs); initData(context); } private void initData(Context context) { LayoutInflater.from(context).inflate(R.layout.jump_layout,this,true);
transformationLoading = (TransformationLoading) findViewById(R.id.transformationLoading);
jumpShadow = (ImageView) findViewById(R.id.jumpShadow);
runnable =new Runnable() {
@Override
public void run() { ViewHelper.setRotation(transformationLoading,180f); ViewHelper.setTranslationY(transformationLoading,0f); ViewHelper. SetScaleX (jumpShadow, 0.2 f); move =true; freeFall(); // Perform the descent animation}}; } // Drop animation private voidfreeFall() {
if (downAnimator ==null) {
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(transformationLoading,"translationY",0,jumpDistance);
ObjectAnimator scaleIndication = ObjectAnimator.ofFloat(jumpShadow,"scaleX"And 0.2 f, 1 f); downAnimator =new AnimatorSet(); downAnimator.playTogether(objectAnimator, scaleIndication); downAnimator.setDuration(time); DownAnimator. SetInterpolator (new AccelerateInterpolator (1.2 f)); downAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
if(move) { transformationLoading.changeShape(); rise(); } } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); } downAnimator.start(); } // Lift animation private voidrise() {
if (upAnimator ==null) {
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(transformationLoading,"translationY",jumpDistance,0);
ObjectAnimator scaleIndication = ObjectAnimator.ofFloat(jumpShadow,"scaleX", 1 f, 0.2 f); ObjectAnimator objectAnimator1 =null; objectAnimator1 = ObjectAnimator.ofFloat(transformationLoading,"rotation",0,angle); upAnimator =new AnimatorSet(); upAnimator.playTogether(objectAnimator, objectAnimator1, scaleIndication); upAnimator.setDuration(time); UpAnimator. SetInterpolator (new DecelerateInterpolator (1.2 f)); upAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
if(move) { freeFall(); } } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); } upAnimator.start(); } public void startAnimate(int delay) {// Start animation with runnable and freeFallif(! move) {if(downAnimator ! =null &&downAnimator.isRunning()) {return;
}
this.removeCallbacks(runnable);
if (delay >0) {
this.postDelayed(runnable, delay);
}else{ this.post(runnable); }}} // Stop animation, stop all animation and listen public voidstopAnimate() {
move =false;
if(upAnimator ! =null) {if (upAnimator.isRunning()) {
upAnimator.cancel();
}
upAnimator.removeAllListeners();
for (Animator animator :upAnimator.getChildAnimations()) {
animator.removeAllListeners();
}
upAnimator =null;
}
if(downAnimator ! =null) {if (downAnimator.isRunning()) {
downAnimator.cancel();
}
downAnimator.removeAllListeners();
for(Animator animator :downAnimator.getChildAnimations()) { animator.removeAllListeners(); } downAnimator =null; } this.removeCallbacks(runnable); } // The interface is loadedonFinishInflate() { super.onFinishInflate(); startAnimate(0); } // The interface is destroyedonDetachedFromWindow() { super.onDetachedFromWindow(); stopAnimate(); }}Copy the code
If you like my article, please click to follow me. The gold mine of Wan Xuandong