每次看到别人做出炫酷的都会想,这个应该很难吧?这是心理上先入为主的就这么认为了,其实实现很简单,下面一步一步的详细剖析自定义圆形进度条的步骤。

首先看效果图:

篇幅有点长,耐心看完肯定get新技能。

看每一个视图都包含了些什么。

  • 最里层一个蓝色圆形
  • 中间一层显示进度的橙色扇形圆弧
  • 最外层一个红色圆环
  • 显示进度百分比的文字以及下方提示文字

下面来一步一步实现:

  1. 创建一个类继承View,并实现几个构造方法
  2. 定义样式属性,获取属性值
  3. 创建画笔
  4. 重写onDraw()绘制
  5. 应用

直接从第二步开始:res->values下创建attrs.xml文件

<?xml version="1.0" encoding="utf-8"?>
<resources><declare-styleable name="CircleProgressView">// 里层实心圆颜色<attr name="circleColor" format="color" />// 中间圆环(宽度/颜色)<attr name="progressWidth" format="dimension" /><attr name="progressColor" format="color" />// 外层圆环(宽度/颜色)<attr name="sectorWidth" format="dimension" /><attr name="sectorColor" format="color" />// 中间进度文字(颜色/大小)<attr name="proTextColor" format="color" /><attr name="proTextSize" format="dimension" />// 中间提示文字(颜色/大小/文本内容)<attr name="tipTextColor" format="color" /><attr name="tipTextSize" format="dimension" /><attr name="tipText" format="string" />// 最大进度<attr name="max" format="integer" />// 是否显示最外层的圆环<attr name="showStoke" format="boolean" />// 进度圆环是否在圆上<attr name="isAbove" format="boolean" />// 进度是否滚动<attr name="isScroll" format="boolean" /></declare-styleable>
</resources>

定义完属性后该获取定义的属性了。 注:上方的文字大小和宽度必须用dimension[尺寸],而不能用float 声明需要的变量

private Paint circlePaint; // 最里层实心圆画笔private int circleColor; // 实心圆颜色private Paint progressPaint; // 中间显示进度圆环画笔private float progressWidth; // 进度圆环宽度private int progressColor; // 进度圆环颜色private Paint sectorPaint; // 最外层圆环画笔private float sectorWidth; // 外层圆环宽度private int sectorColor; // 外层圆环颜色private Paint proTextPaint;private float proTextSize;private int proTextColor;private Paint tipTextPaint;private float tipTextSize;private int tipTextColor;private String tipText;private int currProgress; // 当前进度private int maxProgress; // 最大进度private boolean isShow;private boolean isAbove;private boolean isScroll;

获取属性

private void initAttrs(Context context, AttributeSet attrs) {TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CircleProgressView, 0, 0);circleColor = typedArray.getColor(R.styleable.CircleProgressView_circleColor, 0x993F51B5);progressWidth = typedArray.getDimension(R.styleable.CircleProgressView_progressWidth, 300);progressColor = typedArray.getColor(R.styleable.CircleProgressView_progressColor, 0x3F51B5);sectorWidth = typedArray.getDimension(R.styleable.CircleProgressView_sectorWidth, 10);sectorColor = typedArray.getColor(R.styleable.CircleProgressView_sectorColor, 0xFF4081);proTextSize = typedArray.getDimension(R.styleable.CircleProgressView_proTextSize, 60);proTextColor = typedArray.getColor(R.styleable.CircleProgressView_proTextColor, 0xFFFFFF);tipTextSize = typedArray.getDimension(R.styleable.CircleProgressView_tipTextSize, 30);tipTextColor = typedArray.getColor(R.styleable.CircleProgressView_tipTextColor, 0xFFFFFF);tipText = typedArray.getString(R.styleable.CircleProgressView_tipText);maxProgress = typedArray.getInteger(R.styleable.CircleProgressView_max, 100);isShow = typedArray.getBoolean(R.styleable.CircleProgressView_showStoke, true);isAbove = typedArray.getBoolean(R.styleable.CircleProgressView_isAbove, false);isScroll = typedArray.getBoolean(R.styleable.CircleProgressView_isScroll, false);typedArray.recycle();}

创建5个画笔(1个圆,2个圆环,2个文本),然后在参数最多的构造器中调用

private void initPaint() {// 圆形画笔circlePaint = new Paint();circlePaint.setAntiAlias(true);circlePaint.setStyle(Paint.Style.FILL);// 进度圆环画笔progressPaint = new Paint();progressPaint.setAntiAlias(true);progressPaint.setStyle(Paint.Style.STROKE);// 最外层圆环画笔sectorPaint = new Paint();sectorPaint.setAntiAlias(true);sectorPaint.setStyle(Paint.Style.STROKE);// 进度文字画笔proTextPaint = new Paint();proTextPaint.setAntiAlias(true);proTextPaint.setStyle(Paint.Style.FILL);// 提示文字画笔tipTextPaint = new Paint();tipTextPaint.setAntiAlias(true);tipTextPaint.setStyle(Paint.Style.FILL);}

下面就是最重要的绘制过程了

1、首先将获取到的自定义属性值设置给每个画笔,在onDraw()方法中调用
        2、绘制最里面的蓝色圆形,确定圆心和半径。假设在xml布局中引用了这个View并设置width和height各位100dp,那么圆心就应该在视图的中心位置(50,50),半径则是宽度或者高度的一半(50dp),知道了圆形和半径就可以用canvas.drawCicle()绘制出一个圆。
如下图:

// getWidth为当前View的宽度,即上面设置的100dp
float circleRadius = getWidth() / 2;
// 参数:(圆心X坐标,圆心Y坐标,半径,画笔)
canvas.drawCircle(circleRadius, circleRadius, circleRadius, circlePaint);

这就绘制出了直径为100dp的圆,为灰色矩形的内切圆(矩形加上背景作为对比),如图:

接着再在圆形外面画一个紧贴着的圆环(圆心应该与蓝色圆保持一致),假设圆环的宽度为sectorWidth = 5,想要圆环绘制在灰色矩形内,那么蓝色圆形的半径就应该要缩小,那么需要缩小多少呢?先缩小圆环的宽度来试试。

绘制圆环用的是

canvas.drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)

需要传入5个参数,oval是一个矩形,startAngle为圆环开始的角度,3点钟的方向为0度,sweepAngle为扫过的角度(如360为一周),useCenter为false时绘制的为圆环,为true时绘制的是扇形,paint为画笔。

第一个参数RectF又有几个参数

RectF(float left, float top, float right, float bottom)

当此时我们要在灰色矩形中画圆环,则将灰色矩形左上角的坐标为(0,0),那么这4个参数是用来确定圆环四个边的位置的,如下图:

所以在灰色矩形中并且在蓝色圆形外画圆环就可以这样画,确定圆环的四个顶点位置并且将蓝色圆的半径缩小5,即:

RectF sector = new RectF(0, // left0, // top2 * circleRadius, // right2 * circleRadius); // bottom
// 圆形
canvas.drawCircle(circleRadius, circleRadius, circleRadius - sectorWidth, circlePaint);
// 圆环
canvas.drawArc(sector, 0, 360, false, sectorPaint);

从上图可以看到,圆环是画出来了,也是在蓝色圆形外面,但是感觉好像哪里不对。圆环的宽度有一半好像在矩形外面去了,而圆环与圆之间有空隙,圆环半径应该再缩小圆环宽度/2就刚好填满了空隙,由此我们可以知道 绘制圆环的半径是(蓝色圆的半径+圆环宽度/2),当绘制的圆环有宽度时,圆环的外层要与矩形相切,因此蓝色圆形的半径还需要再缩小圆环宽度/2。

修改以上代码:

RectF sector = new RectF(sectorWidth/2, // leftsectorWidth/2, // top2 * circleRadius - sectorWidth/2, // right2 * circleRadius - sectorWidth/2); // bottom
// 圆形
canvas.drawCircle(circleRadius, circleRadius, circleRadius - sectorWidth/2, circlePaint);
// 圆环
canvas.drawArc(sector, 0, 360, false, sectorPaint);

嗯,Perfect!

接下来在圆与圆环之间再画出一个表示进度的圆弧。
思路很简单,最外层的圆环不用动,将圆形半径缩小圆弧宽度/2即可。绘制圆环和圆弧是一致的,只是扫过的角度不一致而已。

// 几个顶点分别离X,Y轴的距离,progressWidth是进度圆弧的宽度
RectF progressRectF = new RectF(sectorWidth + progressWidth / 2,sectorWidth + progressWidth / 2,2 * circleRadius - sectorWidth - progressWidth / 2,2 * circleRadius - sectorWidth - progressWidth / 2);
// -90度从圆的上顶点开始,扫描90度
canvas.drawArc(progressRectF, -90, 90, false, progressPaint);

如果动态的设置进度。
设: progress // 当前进度
        max // 最大进度百分比(100%则max = 100)
        swapAngel // 扫过的角度

则:swapAngel = (float)progress/max * 360;

canvas.drawArc(progressRectF, -90, swapAngel, false, progressPaint);

Very Nice!!非常简单

接下来就是绘制文字了,将显示进度百分比的文字绘制在圆形的正中央。
绘制文字当然是用canvas.drawText()了,来看看它的几个参数

drawText(String text, float x, float y, Paint paint)

第一个参数是要绘制的文字,第四个参数是画笔,中间2个参数x,y不知道没关系,我们先将x,y设置成圆心的坐标试试看。

我们得到了如图左侧的效果,但是想要的是右侧的效果。对比可知,drawText()中的x,y参数分别指绘制文字左下角的横纵坐标。因此我们需要获取到文字的宽高,
外层圆环半径 - 文字宽度/2,外层圆环半径 + 文字高度/2 就可以将文字移动到最中央。

// 获取文字宽度,proText为绘制的进度文字
int width= proTextPaint.measureText(proText);
// 获取文字高度
Rect rect = new Rect();
proTextPaint.getTextBounds(proText, 0, proText.length(), rect);
int height = rect.height();

获取到宽高之后就可以用drawText绘制出进度文字了。

canvas.drawText(proText, circleRadius - width / 2,circleRadius + height / 2, proTextPaint);

绘制完进度,接下来该继续绘制提示文字了。

将“当前进度”放在下方圆半径中间的位置,根据以上经验可以轻松的写出代码:

Rect tipRect = new Rect();
tipTextPaint.getTextBounds(tipText, 0, tipText.length(), tipRect);
int tipHeight = tipRect.height();
canvas.drawText(tipText, // 绘制的文字circleRadius - tipTextPaint.measureText(tipText) / 2,3 * circleRadius / 2 + tipHeight / 2, tipTextPaint);

至于动态效果是在一个线程中用了一个临时变量 temp 从0 ~ 设置的进度做循环逐渐增加,然后一次一次的绘制出来,不过感觉这样很消耗性能,有更好的办法欢迎联系我交流交流。具体代码就不展示了,欢迎下载Demo看看。

应用

XML :

在XML根元素中声明命名空间(hcc可换)

xmlns:hcc="http://schemas.android.com/apk/res-auto"
<com.cc.customview.progress.CircleProgressViewandroid:id="@+id/pv"android:layout_width="100dp"android:layout_height="100dp"android:layout_centerInParent="true"hcc:isAbove="true"hcc:isScroll="true"hcc:proTextColor="#FFFFFF"hcc:proTextSize="30sp"hcc:progressColor="@color/colorOrange"hcc:progressWidth="5dp"hcc:sectorColor="@color/colorAccent"hcc:showStoke="true"hcc:tipText="当前进度"hcc:max="100" // 默认100,可填其他hcc:tipTextColor="#FFFFFF" />

Java :

pv.setCircleColor(getResources().getColor(R.color.colorPrimary));
pv.setAbove(false);
pv.setScroll(true);
pv.setShow(true);
pv.setProgressColor(getResources().getColor(R.color.colorOrange));
pv.setProTextColor(getResources().getColor(R.color.colorWhite));
pv.setTipTextColor(getResources().getColor(R.color.colorPrimary));
pv.setSectorColor(getResources().getColor(R.color.colorAccent));
pv.setTipText("当前进度");
pv.setTipTextColor(getResources().getColor(R.color.colorWhite));
pv.setProgressWidth(8);
pv.setSectorWidth(5);
pv.setMaxProgress(100); // 默认100,可填其他
CircleProgressView pv= (CircleProgressView) findViewById(R.id.pv);
pv.setProgress(80); // 任意整形大于0的值

Demo下载地址

一步一步带你实现自定义圆形进度条(详解)相关推荐

  1. Android自定义圆形进度条

    Android自定义圆形进度条 github地址:https://github.com/opq1289/CircleProgressView 效果图: 无动画: 有动画: 整圆: 切割圆: 具体步骤: ...

  2. android绘制环形进度_Android动态自定义圆形进度条

    这篇文章主要介绍了Android动态自定义圆形进度条,需要的朋友可以参考下 效果图: A.绘制圆环,圆弧,文本 //1.画圆环 //原点坐标 float circleX = width / 2; fl ...

  3. 微信小程序进度条样式_详解微信小程序——自定义圆形进度条

    微信小程序 自定义圆形进度条,具体如下: 无图无真相,先上图: 实现思路,先绘制底层的灰色圆圈背景,再绘制上层的蓝色进度条. 代码实现: JS代码: 页面初始化 options为页面跳转所带来的参数 ...

  4. 自定义圆形进度条的实现方式

    如何自定义圆形进度条哪,也就是替换一下进度条的图片而已. 先分析一下,系统对进度条如何定义的: 咱们一般情况下载布局文件中这么书写: //在布局文件里的代码<ProgressBarandroid ...

  5. 自定义圆形进度条 自定义倒计时进度条

    自定义圆形进度条 自定义倒计时进度条 版权声明:转载必须注明本文转自严振杰的博客: http://blog.csdn.net/yanzhenjie1003 此控件源码已开源到Github:https: ...

  6. android自定义圆形进度条,实现动态画圆效果

    自定义圆形进度条效果图如下:应用场景如动态显示分数等. view的自定义属性如下attr.xml <?xml version="1.0" encoding="UTF ...

  7. QT自定义圆形进度条

    以下是一个简单的示例,展示如何创建一个自定义的圆形进度条控件. 1.创建一个新的Qt控件类,继承QProgressBar类.在该类的头文件中添加以下代码:     class CircularProg ...

  8. android 环形时间显示_Android_Android实现自定义圆形进度条,今天无意中发现一个圆形进度 - phpStudy...

    Android实现自定义圆形进度条 今天无意中发现一个圆形进度,想想自己实现一个,如下图: 基本思路是这样的: 1.首先绘制一个实心圆 2.绘制一个白色实心的正方形,遮住实心圆 3.在圆的中心动态绘制 ...

  9. Android 自定义View,自定义属性--自定义圆形进度条(整理)

    很多的时候,系统自带的View满足不了我们的功能需求,那么我们就需要自定义View来满足我们的需求 自定义View时要先继承View,添加类的构造方法,重写父类View的一些方法,例如onDraw,为 ...

  10. 【Android 应用开发】 自定义 圆形进度条 组件

    转载著名出处 : http://blog.csdn.net/shulianghan/article/details/40351487 代码下载 : -- CSDN 下载地址 : http://down ...

最新文章

  1. AlexeyAB DarkNet YOLOv3框架解析与应用实践(四)
  2. for android 软件,安卓特工 for Android
  3. dotNet中初始化器的使用
  4. 十个Java基础面试题(附答案)
  5. 华为S5700S-52P-LI-AC千兆网管交换机web登录界面配置
  6. -32767转化为二进制_谁说前端不需要懂二进制
  7. mysql 主从数据库配置_8、MySQL主从数据库配置
  8. 基于springboot的多人聊天系统
  9. PHP 大括号{} 的使用
  10. 整理发布一些关于VMware vSphere的文档
  11. ViewFlipper(翻转视图)使用详解
  12. android 剪切并上传图片
  13. 第7章概率和样本:样本均值的分布
  14. 电脑主板资料库 07【转至www.ongood.com.tw】【FreeXploiT收集整理】
  15. Ubuntu 编译ijkplayer 支持几乎所有格式(MP4,mov,mkv,avi,wmv,m4v,mpg,webm,ogv,3g2.flv,f4v,swf)和https
  16. “蘑菇书“配套在线课程上线
  17. 大数据Spark实战第七集 机器学习和数据处理
  18. 禀赋效应还是锚定效应
  19. 从内盘外盘的变化中看趋势
  20. Grafana 显示某一时间点的总值

热门文章

  1. sybase数据库导出mysql_sybase数据库导出表结构
  2. android最新文献,android开发参考文献
  3. 【Java基础】建立Java面向对象编程OOP模型
  4. 想做电商产品经理你先要学会这几件事
  5. java中json转map
  6. 3dmax 保存慢 卡死
  7. Java基础入门第二版 课后答案
  8. 维修电工技师、高级技师技能实训考核装置
  9. 360 os3.0 android7.1,【360 N6 Pro】360OS安卓7.1系统V3.0.087付费纯净版ROOT刷机包
  10. VMware增强型键盘驱动程序