origin
- The company project uses a progress bar animation, which needs to draw the situation from 0 to 100, and the effect needs to be cool. Finally, Lottie animation + a part of the custom View to achieve, split out. Later, I felt that it was not difficult to achieve such an effect, so I began the following attempts. Let’s take a look at the effect
Analysis of the
- Analysis: the outermost layer is a circle rotated clockwise by small dots, the second layer is a direct circle, and the third layer has two layers: the inner layer is a circle rotated counterclockwise by rectangles, the outer layer is a circle, and finally the text display.
- The first layer is a point of View, drawing is completed using ObjectAnimator rotation; The outer layer and text of the second and third layers are a View, and the inner rectangle of the third layer is a View, which is also drawn and rotated using ObjectAnimator. When it comes to rotation animation, WHEN I want to rotate, I rotate the canvas object, and there is only one canvas object in a View. If everything is drawn on a canvas, it will rotate when I rotate, so I can’t achieve the effect I want. If readers have a better solution, welcome to put forward)
Open dry
The first part
- Thanks to the author of this blog for providing me with a good idea, let me draw inferences from one point of view, if the first part of the reader is very vague, you can enjoy this blog
- How should the first layer of dots and the third layer of inner rectangles be drawn? Here the implementation is more or less the same
- Look at this diagram and you don’t have to be scared. It’s actually quite simple. What we end up drawing on the canvas is circles. Now there are 100 blue circles, 100 hollow circles, each occupying an Angle of 360/200=1.8 degrees; After drawing the blue circle, call Canvas.ratate () and rotate the canvas 3.6 degrees to the center of the next blue circle to start drawing the circle. You’ll end up with 100 blue circles.
- How do I draw a blue circle? You need to determine the center and radius of the blue circle. We can get the width and height of the View in onMeasure(), divide by 2 to get the coordinates of the center of the circle (centerX,centerY), R0 is the radius that can be obtained by the width or height /2, we want to solve r:
R = (R0*sin0.9°)/(1+sin0.9°) — R0 (centerY-R0+r)
- So we can plot it, and we’ll raise the sine of 0.9 degrees to sine of 1 degrees, just to make the blue circle bigger. The key code is as follows :(for aesthetic purposes, I changed 100 small blue circles into 50, and each rotation of 3.6*2=7.2 degrees, that is, the distance between the three small white circles, draw 50 times, each rotation of 7.2 degrees, 50 * 7.2=360 degrees, draw a blue circle each time, and finally 50 small blue circles)
@Override
protected void onDraw(Canvas canvas) {
Log.i("onMeasure"."Execute onDraw");
super.onDraw(canvas);
mSin_1 = (float) Math.sin(Math.toRadians(1)); // Large circle radiusfloatouterRadius = (getWidth() < getHeight() ? getWidth() : getHeight()) / 2f; // Small dot radiusfloat dotRadius = mSin_1 * outerRadius / (1 + mSin_1);
float centerX = getWidth() / 2f;
float centerY = getHeight() / 2f;
mPaint.setStyle(Paint.Style.FILL);
int count = 0;
while(count++ < 50) { canvas.drawCircle(centerX, centerY - outerRadius + dotRadius, dotRadius, mPaint); Canvas. Rotate (7.2 f, centerX, centerY); }}Copy the code
The second part
- The circle formed by drawing rectangles works the same way, but instead of drawing small blue circles, it becomes a drawing rectangle. To draw a rectangle, you need to determine the position of the two points in the upper left and lower right corner. Then rotate the canvas to a certain Angle value and continue to draw. It is drawn 50 times and rotated 10 degrees each time. The total is 500 degrees >360 degrees. If it is greater than 360 degrees, it will be covered repeatedly.
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setShader(null);
mPaint.setAntiAlias(true); // anti-alias mPaint. SetDither (true); // Anti-jitter // the radius is reduced by 40float outerRadius = (getWidth() < getHeight() ? getWidth() : getHeight()) / 2f-40;
float centerX = getWidth() / 2f;
float centerY = getHeight() / 2f;
mPaint.setStyle(Paint.Style.FILL);
int count = 0;
int des = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 6, getResources().getDisplayMetrics());
while(count++ < 50) { canvas.drawRect(centerX-3,centerY-outerRadius,centerX+3,centerY-outerRadius+des, mPaint); Canvas. Rotate (10.0 f, centerX, centerY); }}Copy the code
The third part
- Drawing circles and circles, and drawing text are all done in one View, because there’s no rotation involved, so you can draw them all together. This section is a reference to another blog post, but interested readers can also enjoy a custom progress bar
- CirclePaint. SetStyle (Paint.Style.STROKE); circlePaint. SetStyle (Paint. And it doesn’t contain the center of the circle. For details, see this blog drawArc arc drawing introduction. It is not easy to expand here.
Private void drawCircle(Canvas Canvas, int center, int radius) {// Draw a simple circle firstPaint. // Clear the previous shader firstPaint. SetColor (firstColor); // Set the color of the bottom ring, using the first color firstpaint.setstyle (paint.style.stroke); DrawCircle (Center, center, radius+40, firstPaint); RectF oval = new RectF(center-radius, center-radius, center + radius, center + radius); circlePaint.setShader(null); The Shader class is one of Android's most important graphics transformations. A Shader is called a Shader in 3d software and its function is to color an image. LinearGradient linearGradient = new LinearGradient(circleWidth, circleWidth, getMeasuredWidth() - circleWidth, getMeasuredHeight() - circleWidth, colorArray, null, Shader.TileMode.MIRROR); circlePaint.setShader(linearGradient); Circlepaint.setstyle (paint.style.stroke); circlePaint.setShadowLayer(10, 10, 10, Color.RED); circlePaint.setColor(secondColor); // Set the color of the arc circlepaint.setstrokecap (paint.cap.round); // Calculate the Angle swept each time you draw an arc. Note that the denominator is converted tofloatAlphaAngle = currentValue * 360.0f/maxValue * 1.0f; canvas.drawArc(oval, -90, alphaAngle,false, circlePaint);
}
Copy the code
- As you can see, when I draw the circle, I use alphaAngle, this is the swept Angle, and based on currentValue, the current progress value, I get the current swept Angle. MaxValue is 100. After invalidate() is called, the View redraws, calling onDraw() to get the new alphaAngle value and draw the corresponding progress circle.
public void setProgress(int progress)
{
int percent = progress * maxValue / 100;
if (percent < 0)
{
percent = 0;
}
if(percent > 100) { percent = 100; } this.currentValue = percent; // Update View invalidate(); }Copy the code
- The same goes for drawing text, using the setProgress() method to update the currentValue value. There is one caveat to drawing text here: the text is centered. DrawText () (x,y, center, baseline, x,y); drawText() (x,y, center, baseline, x,y);
private void drawText(Canvas canvas, int center, int radius)
{
floatResult = (currentValue * 100.0f/maxValue * 1.0f); String percent = string.format ("%.1f", result) + "%"; textPaint.setTextAlign(Paint.Align.CENTER); TextPaint. SetColor (textColor); // Set the text color textpaint.settextSize (40); SetStrokeWidth (0); // Set the size of the text to draw. // Note that the width must be reset to 0, otherwise the text will overlap Rect bounds = new Rect(); GetTextBounds (percent, 0, percent.length(), bounds); / / for rendering text the bounding rectangle of the Paint. FontMetricsInt fontMetrics = textPaint. GetFontMetricsInt (); Int baseline = center + (fontMetrics. Bottom-fontmetrics. Top) / 2-fontmetrics. canvas.drawText(percent, center, baseline, textPaint); // Draw text representing progress}Copy the code
conclusion
- In fact, this is not over, just on the inside of the key analysis of the next, the rest of the details of the reader can see the source code, I also encapsulated a dependent library, there is a need to use. Use the corresponding demo on Github, like you can click a star.
- In your project build.gradle add:
allprojects {
repositories {
maven { url 'https://jitpack.io'}}}Copy the code
- Add to build.gradle in module:
implementation 'com. Making. Guoxiaozhou: AnimationDemo: 0.5'
Copy the code
- Demo address: github.com/guoxiaozhou… The project contains the source code of the dependent library, interested can see the details, have a question or error directly contact me