最近项目中使用到了渐变效果的圆形进度条,网上找了很多渐变效果不够圆滑,两个渐变颜色之间有明显的过渡,或者有些代码画出来的效果过渡不美观,于是自己参照写了一个,喜欢的朋友可以参考或者直接使用。
先上一张效果图,视频录制不太好,不过不影响效果
下面开始介绍实现代码,比较简单,直接贴代码吧
1、声明自定义属性
在项目的valuse文件夹下新建attrs.xml,在里面定义自定义控件需要的属性
2、自定义一个进度条roundprogres继承view类
package com.blankj.progressring; import android.animation.valueanimator; import android.content.context; import android.content.res.typedarray; import android.graphics.canvas; import android.graphics.color; import android.graphics.matrix; import android.graphics.paint; import android.graphics.rect; import android.graphics.rectf; import android.graphics.sweepgradient; import android.graphics.typeface; import android.util.attributeset; import android.util.log; import android.view.view; import android.view.animation.linearinterpolator; import org.jetbrains.annotations.nullable; /** * 类描述:渐变的圆形进度条 * * @author:lusy * @date :2018/10/17 */ public class roundprogress extends view { private static final string tag = "roundprogress"; /** * 背景圆环画笔 */ private paint bgpaint; /** * 白色标记画笔 */ private paint iconpaint; /** * 进度画笔 */ private paint progresspaint; /** * 进度文本画笔 */ private paint textpaint; /** * 背景圆环的颜色 */ private int bgcolor; /** * 线条进度的颜色 */ private int iconcolor; private int[] progresscolor; /** * 中间进度百分比的字符串的颜色 */ private int textcolor; /** * 中间进度百分比的字符串的字体大小 */ private float textsize; /** * 圆环的宽度 */ private float roundwidth; /** * 最大进度 */ private int max; /** * 当前进度 */ private float progress; /** * 是否显示中间的进度 */ private boolean textisdisplayable; /** * 圆环半径 */ private int mradius; private int center; private float startangle = -90; private float currentangle; private float currentprogress; public roundprogress(context context) { this(context, null); } public roundprogress(context context, @nullable attributeset attrs) { super(context, attrs); typedarray mtypedarray = context.obtainstyledattributes(attrs, r.styleable.roundprogress); //获取自定义属性和默认值 bgcolor = mtypedarray.getcolor(r.styleable.roundprogress_bgcolor, color.parsecolor("#2d2d2d")); iconcolor = mtypedarray.getcolor(r.styleable.roundprogress_linecolor, color.parsecolor("#ffffff")); textcolor = mtypedarray.getcolor(r.styleable.roundprogress_textcolor, color.parsecolor("#ffffff")); textsize = mtypedarray.getdimension(r.styleable.roundprogress_textsize, 15); roundwidth = mtypedarray.getdimension(r.styleable.roundprogress_roundwidth, 5); max = mtypedarray.getinteger(r.styleable.roundprogress_maxprogress, 100); textisdisplayable = mtypedarray.getboolean(r.styleable.roundprogress_textisdisplayable, true); progresscolor = new int[]{color.parsecolor("#747eff"), color.parsecolor("#0018ff"), color.transparent}; mtypedarray.recycle(); initpaint(); } public roundprogress(context context, @nullable attributeset attrs, int defstyleattr) { super(context, attrs, defstyleattr); } @override protected void onmeasure(int widthmeasurespec, int heightmeasurespec) { //测量控件应占的宽高大小,此处非必需,只是为了确保布局中设置的宽高不一致时仍显示完整的圆 int measurewidth = measurespec.getsize(widthmeasurespec); int measureheight = measurespec.getsize(heightmeasurespec); setmeasureddimension(math.min(measurewidth, measureheight), math.min(measurewidth, measureheight)); } private void initpaint() { bgpaint = new paint(); bgpaint.setstyle(paint.style.stroke); bgpaint.setantialias(true); bgpaint.setcolor(bgcolor); bgpaint.setstrokewidth(roundwidth); iconpaint = new paint(); iconpaint.setstyle(paint.style.stroke); iconpaint.setantialias(true); iconpaint.setcolor(iconcolor); iconpaint.setstrokewidth(roundwidth); progresspaint = new paint(); progresspaint.setstyle(paint.style.stroke); progresspaint.setantialias(true); progresspaint.setstrokewidth(roundwidth); textpaint = new paint(); textpaint.setstyle(paint.style.stroke); textpaint.settypeface(typeface.default_bold); textpaint.setantialias(true); textpaint.setcolor(textcolor); textpaint.settextsize(textsize); textpaint.setstrokewidth(0); } @override protected void ondraw(canvas canvas) { /** * 画最外层的大圆环 */ //获取圆心的x坐标 center = math.min(getwidth(), getheight()) / 2; // 圆环的半径 mradius = (int) (center - roundwidth / 2); rectf oval = new rectf(center - mradius, center - mradius, center mradius, center mradius); //画背景圆环 canvas.drawarc(oval, startangle, 360, false, bgpaint); //画进度圆环 drawprogress(canvas, oval); canvas.drawarc(oval, startangle, currentangle, false, progresspaint); //画白色圆环 float start = startangle currentangle - 1; canvas.drawarc(oval, start, 3, false, iconpaint); //百分比文字 int percent = (int) (((float) progress / (float) max) * 100); //测量字体宽度,我们需要根据字体的宽度设置在圆环中间 string text = string.valueof(percent) "%"; rect textrect = new rect(); textpaint.gettextbounds(text, 0, text.length(), textrect); if (textisdisplayable && percent >= 0) { //画出进度百分比文字 float x = (getwidth() - textrect.width()) / 2; float y = (getheight() textrect.height()) / 2; canvas.drawtext(text, x, y, textpaint); } if (currentprogress < progress) { currentprogress ; postinvalidate(); } } /** * 画进度圆环 * * @param canvas * @param oval */ private void drawprogress(canvas canvas, rectf oval) { float section = progress / 100; currentangle = section * 360; //把需要绘制的角度分成100等分 float unitangle = (float) (currentangle / 100.0); for (float i = 0, end = currentprogress * unitangle; i <= end; i ) { sweepgradient shader = new sweepgradient(center, center, progresscolor, new float[]{0.0f, section, 1.0f}); matrix matrix = new matrix(); matrix.setrotate(startangle, center, center); shader.setlocalmatrix(matrix); progresspaint.setshader(shader); canvas.drawarc(oval, startangle i, 1, false, progresspaint); } } @override protected void onsizechanged(int w, int h, int oldw, int oldh) { super.onsizechanged(w, h, oldw, oldh); //计算外圆半径 宽,高最小值-填充边距/2 center = (math.min(w, h)) / 2; mradius = (int) ((math.min(w, h)) - roundwidth / 2); } public int getmax() { return max; } /** * 设置进度的最大值 * * @param max */ public void setmax(int max) { if (max < 0) { log.e(tag, "max progress not allow <0"); return; } this.max = max; } /** * 获取进度 * * @return */ public float getprogress() { return progress; } /** * 设置进度 * * @param progressvalue * @param useanima 是否需要动画 */ public void setprogress(float progressvalue, boolean useanima) { float percent = progressvalue * max / 100; if (percent < 0) { percent = 0; } if (percent > 100) { percent = 100; } //使用动画 if (useanima) { valueanimator valueanimator = valueanimator.offloat(0, percent); valueanimator.addupdatelistener(new valueanimator.animatorupdatelistener() { @override public void onanimationupdate(valueanimator animation) { progress = (float) animation.getanimatedvalue(); postinvalidate(); } }); valueanimator.setinterpolator(new linearinterpolator()); valueanimator.setduration(1500); valueanimator.start(); } else { this.progress = percent; postinvalidate(); } } public int gettextcolor() { return textcolor; } public void settextcolor(int textcolor) { this.textcolor = textcolor; } public float gettextsize() { return textsize; } public void settextsize(float textsize) { this.textsize = textsize; } public float getroundwidth() { return roundwidth; } public void setroundwidth(float roundwidth) { this.roundwidth = roundwidth; } public int[] getprogresscolor() { return progresscolor; } public void setprogresscolor(int[] progresscolor) { this.progresscolor = progresscolor; postinvalidate(); } public int getbgcolor() { return bgcolor; } public void setbgcolor(int bgcolor) { this.bgcolor = bgcolor; } public int geticoncolor() { return iconcolor; } public void seticoncolor(int iconcolor) { this.iconcolor = iconcolor; } public boolean istextisdisplayable() { return textisdisplayable; } public void settextisdisplayable(boolean textisdisplayable) { this.textisdisplayable = textisdisplayable; } public int getmradius() { return mradius; } public void setmradius(int mradius) { this.mradius = mradius; } public int getcenter() { return center; } public void setcenter(int center) { this.center = center; } public float getstartangle() { return startangle; } public void setstartangle(float startangle) { this.startangle = startangle; } }
3、使用自定义进度条view
activity布局文件使用如下,为了方便测试效果,新增进度加、进度减,修改进度条颜色的按钮