English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Introduction
Everyone knows that security software like Cheetah Clean Master has a function, that is, memory cleaning, which is displayed in the form of a small circular ball to show the size of memory, and the progress of cleaning is shown in the form of percentage numbers and progress bars. This article will give a detailed description of the implementation process of this effect, but will not involve the implementation of memory cleaning.
Preview
Let's first take a look at the final implementation effect (the gif effect is a bit poor):
From the above pictures, we can see that:
① When the Acceleration Ball View is displayed, the progress bar and percentage numbers will start from 0% and increase to a certain value (6(0%).
② After the progress bar stops increasing, the middle circle starts to flip along the Y-axis, and it will flip180 degrees, the percentage numbers above will not appear with a mirror effect (which will be mentioned later).
③ After the user clicks the ball, the memory cleaning process begins, and the progress bar and percentage numbers will go through the process of decreasing to 0 and then increasing to a certain value.
Detailed explanation of the implementation process
In fact, the effect above is implemented by imitating the acceleration ball of猎豹清理大师, with slight differences but roughly the same form. If readers are interested in the above effect, please continue to read on, and the main text will be presented next.
Step 1.Initialization
Firstly, we need to create a new LieBaoView.java, inheriting from View, and we overwrite its constructor as follows:
public LieBaoView(Context context) { super(context); init(); } public LieBaoView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public LieBaoView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); }
Regardless of how the View is instantiated, it will call the init() method, which is mainly used to handle the initialization of various member variables. So, what member variables or instances do we need to help us?
The author's approach is as follows: through a blank bitmap, we draw circles, texts, and so on, and then finally draw this bitmap onto our view.
Therefore, when initializing, we need to obtain instances of various Paints (pens), Bitmaps (blank pictures), Canvases (canvases), and so on. Let's think again: the middle circle can be rotated, so the rotating circle in the middle cannot be placed on the same bitmap as other circles, otherwise it will bring trouble to the subsequent rotation implementation. Therefore, we can prepare two blank bitmaps. Then, we can do it like this:
public void init(){ //Drawing the background circle pen mBackgroundCirclePaint = new Paint(); mBackgroundCirclePaint.setAntiAlias(true); mBackgroundCirclePaint.setColor(Color.argb(0xff, 0x10, 0x53, 0xff)); //Drawing the rotating circle pen mFrontCirclePaint = new Paint(); mFrontCirclePaint.setAntiAlias(true); mFrontCirclePaint.setColor(Color.argb(0xff, 0x5e, 0xae, 0xff)); //Drawing the text pen mTextPaint = new Paint(); mTextPaint.setAntiAlias(true); mTextPaint.setTextSize(80); mTextPaint.setColor(Color.WHITE); //Drawing the progress bar pen mArcPaint = new Paint(); mArcPaint.setAntiAlias(true); mArcPaint.setColor(Color.WHITE); mArcPaint.setStrokeWidth(12); mArcPaint.setStyle(Paint.Style.STROKE); mBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888); mBitmapCanvas = new Canvas(mBitmap); //Associate the canvas with the Bitmap //Rotate the bitmap with the canvas mOverturnBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888); mOverturnBitmapCanvas = new Canvas(mOverturnBitmap); //Some parts have been omitted... //Camera, Matrix, Runnable, and so on will be explained later. mMatrix = new Matrix(); mCamera = new Camera(); }
The above mainly initializes various types of pens, as well as prepares two Bitmaps and their associated canvases. We can draw on the associated canvases, and in this way, we can get two Bitmaps with content.
We continue to think downwards: What else do we need to achieve the flip effect? Android SDK has prepared a set of tools for us: Camera and Matrix. By using these two tools, we can easily perform various transformations on Bitmaps, such as scaling, translation, and flipping. Regarding Camera and Matrix, readers can search for more detailed related knowledge, and this will not be elaborated on here. Finally, we also need Runnable because we need to implement automatic flipping and the automatic increase and decrease of the progress bar. Runnable will be detailed later, so there is no need to hurry. Of course, we also need to set up a click listener.
Step 2.Draw the image
The pen and canvas have been prepared for us. Next, we will draw the required images. We can do this by overriding the View's onDraw() method.
①Draw the background circle, that is, the deepest blue circle in the outermost layer of the figure above:
mBitmapCanvas.drawCircle(mWidth / 2, mHeight / 2, mWidth / 2, mBackgroundCirclePaint);
②Draw the middle white background circle, that is, the white part of the background during the flip process of the rotating circle:
mBitmapCanvas.drawCircle(mWidth / 2, mHeight / 2, mWidth / 2 - mPadding, mTextPaint);
③Draw the progress bar, how to implement the arc-shaped progress bar? Here is the author's idea: implement it through the canvas's drawArc() method, which can draw the largest circle (or ellipse) within a rectangle, set the pen to hollow and the pen line width to12That's it, which can realize a thick arc line. Then, by continuously calling the onDraw() method and modifying the drawArc() angle, the progress bar effect can be achieved. If anyone has other implementation methods, welcome to discuss.
mBitmapCanvas.save(); //Instantiate a rectangle, the coordinates of the top left corner and the bottom right corner of which do not coincide with the original Bitmap, because it is necessary to make //There is a certain gap between the progress bar and the outermost circle RectF rectF = new RectF(10,10,mWidth-10,mHeight-10); //First, rotate the canvas counterclockwise90 degrees, so that the starting angle of drawArc can start from 0 degrees, avoiding unnecessary trouble mBitmapCanvas.rotate(-90, mWidth / 2, mHeight / 2); mBitmapCanvas.drawArc(rectF, 0, ((float)mProgress/mMaxProgress)*360, false, mArcPaint); mBitmapCanvas.restore(); canvas.drawBitmap(mBitmap, 0, 0, null);
④Draw the middle rotating circle. As mentioned earlier, since we need to achieve the flip effect, we can no longer draw on the same Bitmap, so we use another blank Bitmap. The drawing of the rotating circle is very simple, as long as its radius is smaller than the sum of the outer circle radius and the progress bar width:
mOverturnBitmapCanvas.drawCircle(mWidth / 2, mHeight / 2, mWidth / 2 - mPadding, mFrontCirclePaint);
⑤The final step is to draw the percentage numbers on the rotating circle. To draw text, we need to use the Canvas's drawText method, let's focus on this method:
/** * Draw the text, with origin at (x,y), using the specified paint. The * origin is interpreted based on the Align setting in the paint. * * @param text The text to be drawn * @param x The x-coordinate of the origin of the text being drawn * @param y The y-coordinate of the baseline of the text being drawn * @param paint The paint used for the text (e.g. color, size, style) */ public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) { //... }
The first and fourth parameters are not much to say, the second parameter represents the x coordinate where the text starts, and the third parameter represents the y coordinate of the text's baseline. To make the text centered, we just need to set appropriate x, y coordinates. Then, what is the baseline? It actually represents the benchmark point of the text. Let's look at a picture:
From the figure, we can see that the distance from the baseline to the highest point of the text is Ascent, which is a negative value; the distance from the baseline to the lowest point of the text is Descent, which is a positive value. Therefore, if we want to display the text in the control in the center, we can use-(ascent-descent)/2Calculate the half height of the text, and then use mHeight/2(half of the control height) plus this value gives the baseline value in the control, which also achieves centered display. The code is as follows:
String text = (int) (((float)mProgress / mMaxProgress) *100) + "%"; //Obtain the width of the text float textWidth = mTextPaint.measureText(text); //Obtain text specifications Paint.FontMetrics metrics = mTextPaint.getFontMetrics(); float baseLine = mHeight / 2 - (metrics.ascent + metrics.descent) /2; mOverturnBitmapCanvas.drawText(text, mWidth / 2 - textWidth / 2, baseLine, mTextPaint);
Finally, draw the bitmap onto the view:
canvas.drawBitmap(mOverturnBitmap, mMatrix, null);
After the above drawing, let's first see how the effect is:
So the basic effects have been implemented. Next, we will implement the dynamic effect.
Step 3.Implement the automatic flip effect
From the above animation effect, we first let the progress bar increase from 0 to a certain value, and then automatically flip. The implementation of increasing the value is simple, just enable a Runnable, increase the mProgress value within the Runnable, and then call the invalidate() method to refresh the View. Once the progress bar is increased, the flip begins. To flip, we use Camera and Matrix to operate on the middle bitmap, continuously changing the angle to achieve this, let's see the code:
Inside the onDraw() method:
@Override protected void onDraw(Canvas canvas) { //.... //If the current one is rotating if(isRotating) { mCamera.save(); //Rotation Angle mCamera.rotateY(mRotateAngle); //If the rotation angle is greater than or equal to18When 0 degrees, subtract180 degrees if (mRotateAngle >= 180) { mRotateAngle -= 180; } //Get the corresponding matrix according to the Camera's operation mCamera.getMatrix(mMatrix); mCamera.restore(); mMatrix.preTranslate(-mWidth / 2, -mHeight / 2); mMatrix.postTranslate(mWidth / 2, mHeight / 2); } canvas.drawBitmap(mOverturnBitmap, mMatrix, null); //If the current control has not yet been flipped if(!isRotating && !isInital){ //Set isIncreasing to indicate the start of the progress bar's increase process isIncreasing = true; isRotating = true; postDelayed(mRotateRunnable,10); }
Next, let's write mRotateRunnable, with the initialization of Runnable in the init() method:
mRotateRunnable = new Runnable() { @Override public void run() { //If the current state is in an increasing process if(isIncreasing){ Log.d("cylog","mProgress:"+mProgress); //When the progress increases to a certain value, stop the increase if(mProgress >= 59){ isIncreasing = false; } mProgress++; } else { //If the increasing process ends, then start flipping //If mRotateAngle is greater than90 degrees, indicating that the bitmap has been flipped90 degrees, //At this point, the content of the bitmap becomes mirrored content. To avoid the mirrored effect, we need to rotate it back by180 degrees, //At this point, it becomes a normal display, and the extra180 degrees will be subtracted in onDraw. if (mRotateAngle > 90 && mRotateAngle < 180) mRotateAngle = mRotateAngle + 3 + 180; //If mRotateAngle exceeds180 degrees, the flip process is complete else if (mRotateAngle >= 180) { isRotating = false; isInital = true; mRotateAngle = 0; return; } else //Each time the angle increases3This can be fine-tuned, just appropriate mRotateAngle += 3; } invalidate(); //25after ms, call this method again postDelayed(this,25); } };
With the above Runnable and the cooperation of the onDraw() method, the automatic flip effect can already be achieved.
Step 4.Implement the effect of clicking to clean
Alright, let's implement the final effect. Similarly, we use a Runnable to achieve this, as the cleanup effect needs to be initiated by the user clicking on the ball. Therefore, we need an event listener. Inside the onClick method, post a Runnable each time the user clicks.
Firstly, implement mCleaningRunnable within the init() method:
mCleaningRunnable = new Runnable() { @Override public void run() { //If the current progress exceeds a certain value, stop the cleaning if (mProgress >= 60) { isCleaning = false; return; } //If the current state is in a descending process, mProgress continuously decreases until it reaches 0 if (isDescending) { mProgress--; if (mProgress <= 0) isDescending = false; } else { mProgress++; } invalidate(); postDelayed(this,40); } }; setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if(isCleaning) return; //If the current cleanup process is ongoing, return directly to prevent posting too many posts //Set the flag to perform cleanup isDescending = true; isCleaning = true; mProgress--; postDelayed(mCleaningRunnable, 40); } });
The logic has been implemented, where the progress value is continuously reduced to 0 and then increased to a fixed value each time it is clicked, notifying the component to refresh through each call to the invalidate() method, thus achieving a dynamic effect.
Well, so far, all the effects have been implemented, and all the code is posted below. Thank you for reading everyone~
public class LieBaoView extends View { private Paint mBackgroundCirclePaint; private Paint mFrontCirclePaint; private Paint mTextPaint; private Paint mArcPaint; private Bitmap mBitmap; private Bitmap mOverturnBitmap; private Canvas mBitmapCanvas; private Canvas mOverturnBitmapCanvas; private Matrix mMatrix; private Camera mCamera; private int mWidth = 400; private int mHeight = 400; private int mPadding = 20; private int mProgress = 0; private int mMaxProgress = 100; private int mRotateAngle = 0; private Runnable mRotateRunnable; private Runnable mCleaningRunnable; private boolean isRotating; private boolean isInital = false; private boolean isDescending; private boolean isIncreasing; private boolean isCleaning; public LieBaoView(Context context) { super(context); init(); } public LieBaoView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public LieBaoView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(mWidth,mHeight); } public void init(){ //Drawing the background circle pen mBackgroundCirclePaint = new Paint(); mBackgroundCirclePaint.setAntiAlias(true); mBackgroundCirclePaint.setColor(Color.argb(0xff, 0x10, 0x53, 0xff)); //Drawing the rotating circle pen mFrontCirclePaint = new Paint(); mFrontCirclePaint.setAntiAlias(true); mFrontCirclePaint.setColor(Color.argb(0xff, 0x5e, 0xae, 0xff)); //Drawing the text pen mTextPaint = new Paint(); mTextPaint.setAntiAlias(true); mTextPaint.setTextSize(80); mTextPaint.setColor(Color.WHITE); //Drawing the progress bar pen mArcPaint = new Paint(); mArcPaint.setAntiAlias(true); mArcPaint.setColor(Color.WHITE); mArcPaint.setStrokeWidth(12); mArcPaint.setStyle(Paint.Style.STROKE); mBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888); mBitmapCanvas = new Canvas(mBitmap); //Associate the canvas with the Bitmap //Rotate the bitmap with the canvas mOverturnBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888); mOverturnBitmapCanvas = new Canvas(mOverturnBitmap); mMatrix = new Matrix(); mCamera = new Camera(); mRotateRunnable = new Runnable() { @Override public void run() { //If the current state is in an increasing process if(isIncreasing){ Log.d("cylog","mProgress:"+mProgress); //When the progress increases to a certain value, stop the increase if(mProgress >= 59){ isIncreasing = false; } mProgress++; } else { //If the increasing process ends, then start flipping //If mRotateAngle is greater than90 degrees, indicating that the bitmap has been flipped90 degrees, //At this point, the content of the bitmap becomes mirrored content. To avoid the mirrored effect, we need to rotate it back by180 degrees, //At this point, it becomes a normal display, and the extra180 degrees will be subtracted in onDraw. if (mRotateAngle > 90 && mRotateAngle < 180) mRotateAngle = mRotateAngle + 3 + 180; //If mRotateAngle exceeds180 degrees, the flip process is complete else if (mRotateAngle >= 180) { isRotating = false; isInital = true; mRotateAngle = 0; return; } else //Each time the angle increases3This can be fine-tuned, just appropriate mRotateAngle += 3; } invalidate(); //25after ms, call this method again postDelayed(this,25); } }; mCleaningRunnable = new Runnable() { @Override public void run() { //If the current progress exceeds a certain value, stop the cleaning if (mProgress >= 60) { isCleaning = false; return; } //If the current state is in a descending process, mProgress continuously decreases until it reaches 0 if (isDescending) { mProgress--; if (mProgress <= 0) isDescending = false; } else { mProgress++; } invalidate(); postDelayed(this,40); } }; setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if(isCleaning) return; isDescending = true; isCleaning = true; mProgress--; postDelayed(mCleaningRunnable, 40); } }); } @Override protected void onDraw(Canvas canvas) { mBitmapCanvas.drawCircle(mWidth / 2, mHeight / 2, mWidth / 2, mBackgroundCirclePaint); mBitmapCanvas.drawCircle(mWidth / 2, mHeight / 2, mWidth / 2 - mPadding, mTextPaint); mBitmapCanvas.save(); //Instantiate a rectangle, the coordinates of the top left corner and the bottom right corner of which do not coincide with the original Bitmap, because it is necessary to make //There is a certain gap between the progress bar and the outermost circle RectF rectF = new RectF(10,10,mWidth-10,mHeight-10); //First, rotate the canvas counterclockwise90 degrees, so that the starting angle of drawArc can start from 0 degrees, avoiding unnecessary trouble mBitmapCanvas.rotate(-90, mWidth / 2, mHeight / 2); mBitmapCanvas.drawArc(rectF, 0, ((float)mProgress/mMaxProgress)*360, false, mArcPaint); mBitmapCanvas.restore(); canvas.drawBitmap(mBitmap, 0, 0, null); mOverturnBitmapCanvas.drawCircle(mWidth / 2, mHeight / 2, mWidth / 2 - mPadding, mFrontCirclePaint); String text = (int) (((float)mProgress / mMaxProgress) *100) + "%"; //Obtain the width of the text float textWidth = mTextPaint.measureText(text); //Obtain text specifications Paint.FontMetrics metrics = mTextPaint.getFontMetrics(); float baseLine = mHeight / 2 - (metrics.ascent + metrics.descent) /2; mOverturnBitmapCanvas.drawText(text, mWidth / 2 - textWidth / 2, baseLine, mTextPaint); //If the current one is rotating if(isRotating) { mCamera.save(); //Rotation Angle mCamera.rotateY(mRotateAngle); //If the rotation angle is greater than or equal to18When 0 degrees, subtract180 degrees if (mRotateAngle >= 180) { mRotateAngle -= 180; } //Get the corresponding matrix according to the Camera's operation mCamera.getMatrix(mMatrix); mCamera.restore(); mMatrix.preTranslate(-mWidth / 2, -mHeight / 2); mMatrix.postTranslate(mWidth / 2, mHeight / 2); } canvas.drawBitmap(mOverturnBitmap, mMatrix, null); //If the current control has not yet been flipped if(!isRotating && !isInital){ //Set isIncreasing to indicate the start of the progress bar's increase process isIncreasing = true; isRotating = true; postDelayed(mRotateRunnable,10); } } }
That's all for the content of this article. Hope it will be helpful to everyone's learning, and also hope everyone will support the Yelling Tutorial more.
Declaration: The content of this article is from the network, and the copyright belongs to the original author. The content is contributed and uploaded by Internet users spontaneously. This website does not own the copyright, has not been manually edited, and does not assume any relevant legal liability. If you find any content suspected of copyright infringement, please send an email to: notice#oldtoolbag.com (Please replace # with @ when sending an email for reporting, and provide relevant evidence. Once verified, this site will immediately delete the suspected infringing content.)