Android(自定义View实现绚丽的圆形进度条)

本博文包含两个组件,首先上效果图:
1.ProgressBarView1(支持拖动):
Android(自定义View实现绚丽的圆形进度条)
文章图片


2.ProgressBarView2(不同进度值显示不同颜色,不支持拖拽):
Android(自定义View实现绚丽的圆形进度条)
文章图片
Android(自定义View实现绚丽的圆形进度条)
文章图片


代码不多,注释也比较详细,全部贴上了:

(一)ProgressBarView1:

/** * 自定义绚丽的ProgressBar. */ public class ProgressBarView1 extends View { /** * 进度条所占用的角度 */ private static final int ARC_FULL_DEGREE = 300; /** * 弧线的宽度 */ private int STROKE_WIDTH; /** * 组件的宽,高 */ private int width, height; /** * 进度条最大值和当前进度值 */ private float max, progress; /** * 是否允许拖动进度条 */ private boolean draggingEnabled = false; /** * 绘制弧线的矩形区域 */ private RectF circleRectF; /** * 绘制弧线的画笔 */ private Paint progressPaint; /** * 绘制文字的画笔 */ private Paint textPaint; /** * 绘制当前进度值的画笔 */ private Paint thumbPaint; /** * 圆弧的半径 */ private int circleRadius; /** * 圆弧圆心位置 */ private int centerX, centerY; public ProgressBarView1(Context context) { super(context); init(); }public ProgressBarView1(Context context, AttributeSet attrs) { super(context, attrs); init(); }public ProgressBarView1(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); }private void init() { progressPaint = new Paint(); progressPaint.setAntiAlias(true); textPaint = new Paint(); textPaint.setColor(Color.WHITE); textPaint.setAntiAlias(true); thumbPaint = new Paint(); thumbPaint.setAntiAlias(true); //使用自定义字体 textPaint.setTypeface(Typeface.createFromAsset(getContext().getAssets(), "fangz.ttf")); }@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (width == 0 || height == 0) { width = getWidth(); height = getHeight(); //计算圆弧半径和圆心点 circleRadius = Math.min(width, height) / 2; STROKE_WIDTH = circleRadius / 12; circleRadius -= STROKE_WIDTH; centerX = width / 2; centerY = height / 2; //圆弧所在矩形区域 circleRectF = new RectF(); circleRectF.left = centerX - circleRadius; circleRectF.top = centerY - circleRadius; circleRectF.right = centerX + circleRadius; circleRectF.bottom = centerY + circleRadius; } }private Rect textBounds = new Rect(); @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); float start = 90 + ((360 - ARC_FULL_DEGREE) >> 1); //进度条起始点 float sweep1 = ARC_FULL_DEGREE * (progress / max); //进度划过的角度 float sweep2 = ARC_FULL_DEGREE - sweep1; //剩余的角度//绘制起始位置小圆形 progressPaint.setColor(Color.WHITE); progressPaint.setStrokeWidth(0); progressPaint.setStyle(Paint.Style.FILL); float radians = (float) (((360.0f - ARC_FULL_DEGREE) / 2) / 180 * Math.PI); float startX = centerX - circleRadius * (float) Math.sin(radians); float startY = centerY + circleRadius * (float) Math.cos(radians); canvas.drawCircle(startX, startY, STROKE_WIDTH / 2, progressPaint); //绘制进度条 progressPaint.setStrokeWidth(STROKE_WIDTH); progressPaint.setStyle(Paint.Style.STROKE); //设置空心 canvas.drawArc(circleRectF, start, sweep1, false, progressPaint); //绘制进度条背景 progressPaint.setColor(Color.parseColor("#d64444")); canvas.drawArc(circleRectF, start + sweep1, sweep2, false, progressPaint); //绘制结束位置小圆形 progressPaint.setStrokeWidth(0); progressPaint.setStyle(Paint.Style.FILL); float endX = centerX + circleRadius * (float) Math.sin(radians); float endY = centerY + circleRadius * (float) Math.cos(radians); canvas.drawCircle(endX, endY, STROKE_WIDTH / 2, progressPaint); //上一行文字 textPaint.setTextSize(circleRadius >> 1); String text = (int) (100 * progress / max) + ""; float textLen = textPaint.measureText(text); //计算文字高度 textPaint.getTextBounds("8", 0, 1, textBounds); float h1 = textBounds.height(); //% 前面的数字水平居中,适当调整 float extra = text.startsWith("1") ? -textPaint.measureText("1") / 2 : 0; canvas.drawText(text, centerX - textLen / 2 + extra, centerY - 30 + h1 / 2, textPaint); //百分号 textPaint.setTextSize(circleRadius >> 2); canvas.drawText("%", centerX + textLen / 2 + extra + 5, centerY - 30 + h1 / 2, textPaint); //下一行文字 textPaint.setTextSize(circleRadius / 5); text = "可用内存充足"; textLen = textPaint.measureText(text); textPaint.getTextBounds(text, 0, text.length(), textBounds); float h2 = textBounds.height(); canvas.drawText(text, centerX - textLen / 2, centerY + h1 / 2 + h2, textPaint); //绘制进度位置,也可以直接替换成一张图片 float progressRadians = (float) (((360.0f - ARC_FULL_DEGREE) / 2 + sweep1) / 180 * Math.PI); float thumbX = centerX - circleRadius * (float) Math.sin(progressRadians); float thumbY = centerY + circleRadius * (float) Math.cos(progressRadians); thumbPaint.setColor(Color.parseColor("#33d64444")); canvas.drawCircle(thumbX, thumbY, STROKE_WIDTH * 2.0f, thumbPaint); thumbPaint.setColor(Color.parseColor("#99d64444")); canvas.drawCircle(thumbX, thumbY, STROKE_WIDTH * 1.4f, thumbPaint); thumbPaint.setColor(Color.WHITE); canvas.drawCircle(thumbX, thumbY, STROKE_WIDTH * 0.8f, thumbPaint); }private boolean isDragging = false; @Override public boolean onTouchEvent(@NonNull MotionEvent event) { if (!draggingEnabled) { return super.onTouchEvent(event); }//处理拖动事件 float currentX = event.getX(); float currentY = event.getY(); int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: //判断是否在进度条thumb位置 if (checkOnArc(currentX, currentY)) { float newProgress = calDegreeByPosition(currentX, currentY) / ARC_FULL_DEGREE * max; setProgressSync(newProgress); isDragging = true; } break; case MotionEvent.ACTION_MOVE: if (isDragging) { //判断拖动时是否移出去了 if (checkOnArc(currentX, currentY)) { setProgressSync(calDegreeByPosition(currentX, currentY) / ARC_FULL_DEGREE * max); } else { isDragging = false; } } break; case MotionEvent.ACTION_UP: isDragging = false; break; }return true; }private float calDistance(float x1, float y1, float x2, float y2) { return (float) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); }/** * 判断该点是否在弧线上(附近) */ private boolean checkOnArc(float currentX, float currentY) { float distance = calDistance(currentX, currentY, centerX, centerY); float degree = calDegreeByPosition(currentX, currentY); return distance > circleRadius - STROKE_WIDTH * 5 && distance < circleRadius + STROKE_WIDTH * 5 && (degree >= -8 && degree <= ARC_FULL_DEGREE + 8); }/** * 根据当前位置,计算出进度条已经转过的角度。 */ private float calDegreeByPosition(float currentX, float currentY) { float a1 = (float) (Math.atan(1.0f * (centerX - currentX) / (currentY - centerY)) / Math.PI * 180); if (currentY < centerY) { a1 += 180; } else if (currentY > centerY && currentX > centerX) { a1 += 360; }return a1 - (360 - ARC_FULL_DEGREE) / 2; }public void setMax(int max) { this.max = max; invalidate(); }public void setProgress(float progress) { final float validProgress = checkProgress(progress); //动画切换进度值 new Thread(new Runnable() { @Override public void run() { float oldProgress = ProgressBarView1.this.progress; for (int i = 1; i <= 100; i++) { ProgressBarView1.this.progress = oldProgress + (validProgress - oldProgress) * (1.0f * i / 100); postInvalidate(); SystemClock.sleep(20); } } }).start(); }public void setProgressSync(float progress) { this.progress = checkProgress(progress); invalidate(); }//保证progress的值位于[0,max] private float checkProgress(float progress) { if (progress < 0) { return 0; }return progress > max ? max : progress; }public void setDraggingEnabled(boolean draggingEnabled) { this.draggingEnabled = draggingEnabled; } }

(二)ProgressBarView2:

/** * 自定义绚丽的ProgressBar. */ public class ProgressBarView2 extends View { /** * 进度条所占用的角度 */ private static final int ARC_FULL_DEGREE = 300; //进度条个数 private static final int COUNT = 100; //每个进度条所占用角度 private static final float ARC_EACH_PROGRESS = ARC_FULL_DEGREE * 1.0f / (COUNT - 1); /** * 弧线细线条的长度 */ private int ARC_LINE_LENGTH; /** * 弧线细线条的宽度 */ private int ARC_LINE_WIDTH; /** * 组件的宽,高 */ private int width, height; /** * 进度条最大值和当前进度值 */ private float max, progress; /** * 绘制弧线的画笔 */ private Paint progressPaint; /** * 绘制文字的画笔 */ private Paint textPaint; /** * 绘制文字背景圆形的画笔 */ private Paint textBgPaint; /** * 圆弧的半径 */ private int circleRadius; /** * 圆弧圆心位置 */ private int centerX, centerY; public ProgressBarView2(Context context) { super(context); init(); }public ProgressBarView2(Context context, AttributeSet attrs) { super(context, attrs); init(); }public ProgressBarView2(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); }private void init() { progressPaint = new Paint(); progressPaint.setAntiAlias(true); textPaint = new Paint(); textPaint.setColor(Color.WHITE); textPaint.setAntiAlias(true); textBgPaint = new Paint(); textBgPaint.setAntiAlias(true); }@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (width == 0 || height == 0) { width = getWidth(); height = getHeight(); //计算圆弧半径和圆心点 circleRadius = Math.min(width, height) / 2; ARC_LINE_LENGTH = circleRadius / 6; ARC_LINE_WIDTH = ARC_LINE_LENGTH / 8; centerX = width / 2; centerY = height / 2; } }private Rect textBounds = new Rect(); @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); float start = (360 - ARC_FULL_DEGREE) >> 1; //进度条起始角度 float sweep1 = ARC_FULL_DEGREE * (progress / max); //进度划过的角度//绘制进度条 progressPaint.setColor(Color.parseColor(calColor(progress / max, "#ffff0000", "#ff00ff00"))); progressPaint.setStrokeWidth(ARC_LINE_WIDTH); float drawDegree = 1.6f; while (drawDegree <= ARC_FULL_DEGREE) { double a = (start + drawDegree) / 180 * Math.PI; float lineStartX = centerX - circleRadius * (float) Math.sin(a); float lineStartY = centerY + circleRadius * (float) Math.cos(a); float lineStopX = lineStartX + ARC_LINE_LENGTH * (float) Math.sin(a); float lineStopY = lineStartY - ARC_LINE_LENGTH * (float) Math.cos(a); if (drawDegree > sweep1) { //绘制进度条背景 progressPaint.setColor(Color.parseColor("#88aaaaaa")); progressPaint.setStrokeWidth(ARC_LINE_WIDTH >> 1); } canvas.drawLine(lineStartX, lineStartY, lineStopX, lineStopY, progressPaint); drawDegree += ARC_EACH_PROGRESS; }//绘制文字背景圆形 textBgPaint.setStyle(Paint.Style.FILL); //设置填充 textBgPaint.setColor(Color.parseColor("#41668b")); canvas.drawCircle(centerX, centerY, (circleRadius - ARC_LINE_LENGTH) * 0.8f, textBgPaint); textBgPaint.setStyle(Paint.Style.STROKE); //设置空心 textBgPaint.setStrokeWidth(2); textBgPaint.setColor(Color.parseColor("#aaaaaaaa")); canvas.drawCircle(centerX, centerY, (circleRadius - ARC_LINE_LENGTH) * 0.8f, textBgPaint); //上一行文字 textPaint.setTextSize(circleRadius >> 1); String text = (int) (100 * progress / max) + ""; float textLen = textPaint.measureText(text); //计算文字高度 textPaint.getTextBounds("8", 0, 1, textBounds); float h1 = textBounds.height(); canvas.drawText(text, centerX - textLen / 2, centerY - circleRadius / 10 + h1 / 2, textPaint); //分 textPaint.setTextSize(circleRadius >> 3); textPaint.getTextBounds("分", 0, 1, textBounds); float h11 = textBounds.height(); canvas.drawText("分", centerX + textLen / 2 + 5, centerY - circleRadius / 10 + h1 / 2 - (h1 - h11), textPaint); //下一行文字 textPaint.setTextSize(circleRadius / 6); text = "点击优化"; textLen = textPaint.measureText(text); canvas.drawText(text, centerX - textLen / 2, centerY + circleRadius / 2.5f, textPaint); }public void setMax(int max) { this.max = max; invalidate(); }//动画切换进度值(异步) public void setProgress(final float progress) { new Thread(new Runnable() { @Override public void run() { float oldProgress = ProgressBarView2.this.progress; for (int i = 1; i <= 100; i++) { ProgressBarView2.this.progress = oldProgress + (progress - oldProgress) * (1.0f * i / 100); postInvalidate(); SystemClock.sleep(20); } } }).start(); }//直接设置进度值(同步) public void setProgressSync(float progress) { this.progress = progress; invalidate(); }/** * 计算渐变效果中间的某个颜色值。 * 仅支持 #aarrggbb 模式,例如 #ccc9c9b2 */ public String calColor(float fraction, String startValue, String endValue) { int start_a, start_r, start_g, start_b; int end_a, end_r, end_g, end_b; //start start_a = getIntValue(startValue, 1, 3); start_r = getIntValue(startValue, 3, 5); start_g = getIntValue(startValue, 5, 7); start_b = getIntValue(startValue, 7, 9); //end end_a = getIntValue(endValue, 1, 3); end_r = getIntValue(endValue, 3, 5); end_g = getIntValue(endValue, 5, 7); end_b = getIntValue(endValue, 7, 9); return "#" + getHexString((int) (start_a + fraction * (end_a - start_a))) + getHexString((int) (start_r + fraction * (end_r - start_r))) + getHexString((int) (start_g + fraction * (end_g - start_g))) + getHexString((int) (start_b + fraction * (end_b - start_b))); }//从原始#AARRGGBB颜色值中指定位置截取,并转为int. private int getIntValue(String hexValue, int start, int end) { return Integer.parseInt(hexValue.substring(start, end), 16); }private String getHexString(int value) { String a = Integer.toHexString(value); if (a.length() == 1) { a = "0" + a; }return a; } }





【Android(自定义View实现绚丽的圆形进度条)】

    推荐阅读