Requirements analysis and implementation planning
Introduction
Recently i have use douban fm app and i like the circularity progress bar in this app, so i think to write a same one.
There is the progressbar in douban fm:
functional analysis
Although the function is simple, but still needs careful analysis
1.There is a circul outside of the icon, You can set the width
2.Circular progress bar and background at the bottom,you can set the width, color, and so on
3.The inside has a rounded picture, can rotate
Analysis for implementation
1.The circle that you can set the width
This is easy,You can use canvas to draw directly in the ondraw Method,of course,we need careful on radiu and padding,the control itself is actually a rectangle,we need to select a shorter as the diameter,Also have to deal with internal padding
2.Circular progress bar and progress bar at the bottom, you can set the width, color, and so on
This can be achieved with canvas drawarc Method,showing the progressbar by draw different length of arch,but it is important to note,We need to calculate the radius of the arc, and start and end points.
3.The inside has a rounded picture, can be rotated
This demand can be divided into three parts, there are pictures, round, you can rotate
First a picture,Simple,canvas and drawbitmap can draw it(canvas is so strong)
Round,still use canvas to control bitmap to draw it, you can see the detail in code
finally can rotate,we can use canvas rotate method to implement it.
show
Here is a show of the final look
That is my own project
here
code
below is the code and some comment
Our main work It was in a custom view's onDraw method,so,We need to have a child class that inherits the view class,MyProgress, we'll call him
we show the MyProgress's onDraw method
1.a circule can set width
This is easy,You can use canvas to draw directly in the ondraw Method,of course,we need careful on radiu and padding
super.onDraw(canvas); //need invoke parent's onDraw
final int paddingLeft = getPaddingLeft();
final int paddingRight = getPaddingRight();
final int paddingTop = getPaddingTop();
final int paddingBottom = getPaddingBottom(); //get padding
//get the view's width and height and decide the radiu
int width = getWidth() - paddingLeft - paddingRight;
int height = getHeight() - paddingTop - paddingBottom;
radiu = Math.min(width , height) / 2 - boundWidth - progressWidth; //calculate radius,chose the short one,boundWidth is the circul's width,progressWidth is the progress bar's width
//setup the paint
paint.setStyle(Paint.Style.STROKE); //Set the paint to outline
paint.setStrokeWidth(boundWidth); //set width
paint.setColor(Color.BLACK); //set color
//draw the inner circle
int centerX = paddingLeft + getWidth()/2;
int centerY = paddingTop + getHeight() / 2; //Calculation of the center point of the circle
canvas.drawCircle(centerX,centerY, radiu, paint); //draw circular
2.Circular progress bar and progress bar at the bottom, you can set the width, color, and so on
We need change the start and end point of the arch
//set paint for arc
paint.setStrokeWidth(progressWidth);
paint.setStrokeCap(Paint.Cap.ROUND);//Sets the width of the progress, setting the end is a circular arc
//prepare for draw arc
RectF oval = new RectF();
oval.left = centerX -totalRadiu ;
oval.top =centerY- totalRadiu ;
oval.right = centerX + totalRadiu;
oval.bottom = centerY+ totalRadiu; //A new ellipse, sets the coordinates of four points
paint.setColor(progressBackColor);//Sets the background color of the progress bar
//draw background arc
canvas.drawArc(oval, arcStar, arcEnd, false, paint); //Draw a circular arc at the bottom, as a background
//draw progress arc
paint.setColor(progressColor);//Sets the color of the progress bar
canvas.drawArc(oval, arcStar, progress, false, paint);//draw progress bar
3.The inside has a rounded picture, can be rotated
float totalRadiu = radiu +boundWidth +progressWidth/2;//set outside radiu
//draw the circlr pic
if (drawable != null&&bitmap == null) {
image = ((BitmapDrawable) drawable).getBitmap();//get bitmap resource
bitmap = Bitmap.createBitmap((int)(2*totalRadiu),(int)(2*totalRadiu), Bitmap.Config.ARGB_8888);
Canvas bitmapCanvas = new Canvas(bitmap);//create a bitmap and a canvas to control it
Paint bitmapPaint = new Paint();
bitmapPaint.setAntiAlias(true);//create a new paint and set antialias
bitmapCanvas.drawCircle(totalRadiu, totalRadiu, radiu, bitmapPaint);//draw a circular
bitmapPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));//key code,Set to intersect mode,and later there will be content to get the intersection
bitmapCanvas.drawBitmap(image,null,new RectF(0,0,2*totalRadiu,2*totalRadiu) , bitmapPaint);//Draw your own picture to an existing canvas
}
Rect rect = new Rect((int)(centerX -totalRadiu),(int)(centerY-totalRadiu),(int)(centerX+totalRadiu),(int)(centerY+ totalRadiu));//Create a new RECT, set the boundary point
canvas.save();
if(isRotate)
canvas.rotate(rotateDegree,centerX,centerY);//Set the rotation, in order to achieve picture rotation effect, rotateDegree is the rotation angle
canvas.drawBitmap(bitmap,null ,rect, paint);//Draw picture
so that's our ondraw method
full code
MyProgress.java
public class MyProgressBar extends View {
float progress = 360;
float arcStar = 270;
float arcEnd = 360;
double rotateStep = 0.2;
Bitmap bitmap;
int totalTime;
Bitmap image;
Drawable drawable;
int boundWidth = 5;
private int progressWidth = 30;
private boolean isRotate = false;
private int progressColor = Color.GREEN;
private int progressBackColor = Color.GREEN;
private float rotateDegree = 0;
public MyProgressBar(Context context) {
super(context);
}
public MyProgressBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private float radiu;
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
public void setRadiu(float radiu) {
this.radiu = radiu;
invalidate();
}
//start 函数使用 countDownTimer类来更新progress和旋转角度
public void start(long time) {
bitmap = null;
time *= 60000;
final float step = (float) 360 / (time / 30);
CountDownTimer mTimer = new CountDownTimer(time, 30) {
public void onTick(long millisUntilFinished) {
progress -= step;
rotateDegree -= rotateStep;
invalidate();
}
@Override
public void onFinish() {
end(step);
}
};
mTimer.start();
}
private void end(float step) {
progress -= step;
invalidate();
progress = 0;
rotateDegree = 0;
invalidate();
}
public void setBoundWidth(int width) {
boundWidth = width;
}
public void setProgressWidth(int width) {
progressWidth = width;
}
public void setProgressColor(int color) {
progressColor = color;
}
public void setProgressBackColor(int color) {
progressBackColor = color;
}
public void setDrawable(Drawable drawable) {
this.drawable = drawable;
invalidate();
}
public void setIsRote(boolean rotate)
{
this.isRotate = rotate;
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
final int paddingLeft = getPaddingLeft();
final int paddingRight = getPaddingRight();
final int paddingTop = getPaddingTop();
final int paddingBottom = getPaddingBottom();
//get the view's width and height and decide the radiu
int width = getWidth() - paddingLeft - paddingRight;
int height = getHeight() - paddingTop - paddingBottom;
radiu = Math.min(width , height) / 2 - boundWidth - progressWidth;
//setup the paint
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(boundWidth);
paint.setColor(Color.BLACK);
//draw the inner circle
int centerX = paddingLeft + getWidth()/2;
int centerY = paddingTop + getHeight() / 2;
canvas.drawCircle(centerX,centerY, radiu, paint);
float totalRadiu = radiu +boundWidth +progressWidth/2;
//draw the circlr pic
if (drawable != null&&bitmap == null) {
image = ((BitmapDrawable) drawable).getBitmap();
bitmap = Bitmap.createBitmap((int)(2*totalRadiu),(int)(2*totalRadiu), Bitmap.Config.ARGB_8888);
Canvas bitmapCanvas = new Canvas(bitmap);
Paint bitmapPaint = new Paint();
bitmapPaint.setAntiAlias(true);
bitmapCanvas.drawCircle(totalRadiu, totalRadiu, radiu, bitmapPaint);
bitmapPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
bitmapCanvas.drawBitmap(image,null,new RectF(0,0,2*totalRadiu,2*totalRadiu) , bitmapPaint);
}
Rect rect = new Rect((int)(centerX -totalRadiu),(int)(centerY-totalRadiu),(int)(centerX+totalRadiu),(int)(centerY+ totalRadiu));
canvas.save();
if(isRotate)
canvas.rotate(rotateDegree,centerX,centerY);
canvas.drawBitmap(bitmap,null ,rect, paint);
canvas.restore();
//set paint for arc
paint.setStrokeWidth(progressWidth);
paint.setStrokeCap(Paint.Cap.ROUND);
//prepare for draw arc
RectF oval = new RectF();
oval.left = centerX -totalRadiu ;
oval.top =centerY- totalRadiu ;
oval.right = centerX + totalRadiu;
oval.bottom = centerY+ totalRadiu;
paint.setColor(progressBackColor);
//draw background arc
canvas.drawArc(oval, arcStar, arcEnd, false, paint);
//draw progress arc
paint.setColor(progressColor);
canvas.drawArc(oval, arcStar, progress, false, paint);
}
}
You can find the project on GitHub
here
summary
There are some problem worth to think
1.on clipping pictrue,need to note the canvas's coodinate Is relative to the picture,Instead of the global coordinates
2.when draw,need reduce draw a pic multiple time,Such as cropping rounded picture ,one time is enough.
3.For the few key points coordinates of the Custom View,should use a clarity way to present.
4.You can if you start to do