Android编程基于自定义View实现绚丽的圆形进度条功能示例
发布时间 - 2026-01-10 22:41:38 点击率:次本文实例讲述了Android编程基于自定义View实现绚丽的圆形进度条功能。分享给大家供大家参考,具体如下:

本文包含两个组件,首先上效果图:
1.ProgressBarView1(支持拖动):
2.ProgressBarView2(不同进度值显示不同颜色,不支持拖拽):
代码不多,注释也比较详细,全部贴上了:
(一)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相关内容感兴趣的读者可查看本站专题:《Android视图View技巧总结》、《Android开发动画技巧汇总》、《Android编程之activity操作技巧总结》、《Android布局layout技巧总结》、《Android开发入门与进阶教程》、《Android资源操作技巧汇总》及《Android控件用法总结》
希望本文所述对大家Android程序设计有所帮助。
# Android
# 自定义
# View
# 圆形
# 进度条
# Android动态自定义圆形进度条
# Android编程之ProgressBar圆形进度条颜色设置方法
# Android带圆形数字进度的自定义进度条示例
# Android实现带数字的圆形进度条(自定义进度条)
# Android编程实现WebView添加进度条的方法
# Android自定义控件实现圆形进度条
# Android自定义Material进度条效果
# Android自定义view实现进度条指示效果
# android自定义view制作圆形进度条效果
# Android实现环形进度条的实例
# Android编程实现类似于圆形ProgressBar的进度条效果
# 拖动
# 进阶
# 细线
# 操作技巧
# 相关内容
# 去了
# 不多
# 感兴趣
# 给大家
# 可以直接
# 不支持
# 更多关于
# 贴上
# 所述
# 程序设计
# 计算出
# 判断是否
# 拖拽
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
PHP的CURL方法curl_setopt()函数案例介绍(抓取网页,POST数据)
如何用AWS免费套餐快速搭建高效网站?
JavaScript 输出显示内容(document.write、alert、innerHTML、console.log)
公司门户网站制作流程,华为官网怎么做?
猎豹浏览器开发者工具怎么打开 猎豹浏览器F12调试工具使用【前端必备】
C++时间戳转换成日期时间的步骤和示例代码
大连 网站制作,大连天途有线官网?
Laravel怎么发送邮件_Laravel Mail类SMTP配置教程
如何在香港免费服务器上快速搭建网站?
Laravel路由Route怎么设置_Laravel基础路由定义与参数传递规则【详解】
JavaScript Ajax实现异步通信
UC浏览器如何设置启动页 UC浏览器启动页设置方法
魔毅自助建站系统:模板定制与SEO优化一键生成指南
如何在万网自助建站中设置域名及备案?
如何实现建站之星域名转发设置?
详解CentOS6.5 安装 MySQL5.1.71的方法
Laravel Seeder怎么填充数据_Laravel数据库填充器的使用方法与技巧
弹幕视频网站制作教程下载,弹幕视频网站是什么意思?
jimdo怎样用html5做选项卡_jimdo选项卡html5实现与切换效果【指南】
微信小程序 五星评分(包括半颗星评分)实例代码
Laravel如何处理异常和错误?(Handler示例)
Laravel如何使用Spatie Media Library_Laravel图片上传管理与缩略图生成【步骤】
Laravel如何正确地在控制器和模型之间分配逻辑_Laravel代码职责分离与架构建议
如何获取上海专业网站定制建站电话?
如何在腾讯云服务器上快速搭建个人网站?
微博html5版本怎么弄发语音微博_语音录制入口及时长限制操作【教程】
Laravel如何实现多级无限分类_Laravel递归模型关联与树状数据输出【方法】
JavaScript中的标签模板是什么_它如何扩展字符串功能
JavaScript如何实现错误处理_try...catch如何捕获异常?
黑客如何利用漏洞与弱口令入侵网站服务器?
如何快速生成可下载的建站源码工具?
如何自定义safari浏览器工具栏?个性化设置safari浏览器界面教程【技巧】
图片制作网站免费软件,有没有免费的网站或软件可以将图片批量转为A4大小的pdf?
JS中使用new Date(str)创建时间对象不兼容firefox和ie的解决方法(两种)
如何在IIS中新建站点并配置端口与IP地址?
使用C语言编写圣诞表白程序
使用豆包 AI 辅助进行简单网页 HTML 结构设计
如何获取免费开源的自助建站系统源码?
如何自定义建站之星模板颜色并下载新样式?
Laravel怎么做数据加密_Laravel内置Crypt门面的加密与解密功能
Laravel如何使用Facades(门面)及其工作原理_Laravel门面模式与底层机制
如何用JavaScript实现文本编辑器_光标和选区怎么处理
Laravel如何处理跨站请求伪造(CSRF)保护_Laravel表单安全机制与令牌校验
Laravel怎么实现前端Toast弹窗提示_Laravel Session闪存数据Flash传递给前端【方法】
如何自定义建站之星网站的导航菜单样式?
Gemini怎么用新功能实时问答_Gemini实时问答使用【步骤】
怎么制作网站设计模板图片,有电商商品详情页面的免费模板素材网站推荐吗?
海南网站制作公司有哪些,海口网是哪家的?
香港服务器建站指南:免备案优势与SEO优化技巧全解析
Laravel用户密码怎么加密_Laravel Hash门面使用教程

