最近项目有要用到环形的进度条,Github上有一个类似的DashedCircularProgress控件,但是他画的进度是通过设置画笔的虚线效果来实现间隔的:progresspaint.setPathEffect(new DashPathEffect(new float[]{dashWith, dashSpace}, dashSpace));如果内层还有一层圆环,在动态设置时,内层和外层有细微的偏差.于是我在原有基础上改了一个,实现了我要的效果(设置进度时可以选择加动画或者不加动画):
控件实现:
这个控件继承RelativeLayout,在onDraw时做了两件事:
1、先画出底部的黑色环形;
2、按照当时的进度值画出对应比例的外层绿色环形.
对外提供一个接口,回调当前进度值:
public interface OnValuechangelistener { void onValueChange(float value); }
核心绘制类:
InternalCirclePainterImp2,绘制内层的黑色的环形:
/** * @author Chuck */ public class InternalCirclePainterImp2 implements InternalCirclePainter { private RectF internalCircle;//画出圆弧时,圆弧的外切矩形 private Paint internalCirclePaint; private int color; private float startAngle = 270f; int arcQuantity=100;//等分(圆弧加间隔),比如arcQuantity=100时,表示将有100个圆弧,和100个空白间隔 float ratio=0.5f;//每段圆弧与圆弧加间隔之和的比例,ratio=0.5表示每个圆弧与相邻的间隔弧度比是1:1 private int width; private int height; private int internalstrokeWidth = 48;//圆环宽度 public InternalCirclePainterImp2(int color, int progressstrokeWidth, int arcQuantity,float ratio) { this.color = color; this.internalstrokeWidth = progressstrokeWidth; this.arcQuantity = arcQuantity; if(ratio>0&&ratio<1){ this.ratio = ratio; } init(); } private void init() { initExternalCirclePainter(); } private void initExternalCirclePainter() { internalCirclePaint = new Paint(); internalCirclePaint.setAntiAlias(true); internalCirclePaint.setstrokeWidth(internalstrokeWidth); internalCirclePaint.setColor(color); internalCirclePaint.setStyle(Paint.Style.stroke); } //圆弧外切矩形 private void initExternalCircle() { internalCircle = new RectF(); float padding = internalstrokeWidth * 0.5f; internalCircle.set(padding, padding , width - padding, height - padding); initExternalCirclePainter(); } @Override public void draw(Canvas canvas) { float eachAngle=360f/arcQuantity; float eachArcAngle=eachAngle*ratio; for(int i=0;i<arcQuantity*2;i++){ if(i%2==0){//遇到偶数就画圆弧,基数则跳过 canvas.drawArc(internalCircle, startAngle+eachAngle*i/2, eachArcAngle, false, internalCirclePaint); } else{ continue; } } } public void setColor(int color) { this.color = color; internalCirclePaint.setColor(color); } @Override public int getColor() { return color; } @Override public void onSizeChanged(int height, int width) { this.width = width; this.height = height; initExternalCircle(); } }
ProgresspainterImp2,绘制内层的黑色的环形:
/** * @author Chuck */ public class ProgresspainterImp2 implements Progresspainter { private RectF progressCircle; private Paint progresspaint; private int color = Color.RED; private float startAngle = 270f; private int internalstrokeWidth = 48; private float min; private float max; private int width; private int height; private int currentPecent;//当前的百分比 int arcQuantity=100;//等分(圆弧加间隔),比如arcQuantity=100时,表示将有100个圆弧,和100个空白间隔 float ratio=0.5f;//每段圆弧与圆弧加间隔之和的比例,ratio=0.5表示每个圆弧与相邻的间隔弧度比是1:1 public ProgresspainterImp2(int color, float min, float max, int progressstrokeWidth, int arcQuantity,float ratio) { this.color = color; this.min = min; this.max = max; this.internalstrokeWidth = progressstrokeWidth; this.arcQuantity = arcQuantity; this.ratio = ratio; init(); Log.e(ProgresspainterImp,构造函数执行); } private void init() { initInternalCirclePainter(); } private void initInternalCirclePainter() { progresspaint = new Paint(); progresspaint.setAntiAlias(true); progresspaint.setstrokeWidth(internalstrokeWidth); progresspaint.setColor(color); progresspaint.setStyle(Paint.Style.stroke); } //初始化外切的那个矩形 private void initInternalCircle() { progressCircle = new RectF(); float padding = internalstrokeWidth * 0.5f; progressCircle.set(padding, padding , width - padding, height - padding); initInternalCirclePainter(); } @Override public void draw(Canvas canvas) { float eachAngle=360f/arcQuantity; float eachArcAngle=eachAngle*ratio; int quantity=2*arcQuantity*currentPecent/100; for(int i=0;i<quantity;i++){ if(i%2==0){//遇到偶数就画圆弧,基数则跳过 canvas.drawArc(progressCircle, startAngle+eachAngle*i/2, eachArcAngle, false, progresspaint); } else{ continue; } } } public float getMin() { return min; } public void setMin(float min) { this.min = min; } public float getMax() { return max; } public void setMax(float max) { this.max = max; } public void setValue(float value) { this.currentPecent = (int) (( 100f * value) / max); } @Override public void onSizeChanged(int height, int width) { Log.e(ProgresspainterImp,onSizeChanged执行); this.width = width; this.height = height; initInternalCircle(); } public int getColor() { return color; } public void setColor(int color) { this.color = color; progresspaint.setColor(color); } }
<declare-styleable name=CircularLoadingView> <attr name=base_color format=color /> <!--内层圆环的颜色--> <attr name=progress_color format=color /><!--进度圆环的颜色--> <attr name=max format=float /><!--最小值--> <attr name=min format=float /><!--最大值--> <attr name=duration format=integer /><!--动画时长--> <attr name=progress_stroke_width format=integer /><!--圆环宽度--> <!--等分(圆弧加间隔),比如arcQuantity=100时,表示将有100个圆弧,和100个空白间隔--> <attr name=argQuantity format=integer /> <!--每段圆弧与圆弧加间隔之和的比例,ratio=0.5表示每个圆弧与相邻的间隔弧度比是1:1--> <attr name=ratio format=float /> </declare-styleable>
调用:
main_activity.xml:
<?xml version=1.0 encoding=utf-8?> <RelativeLayout xmlns:android=http://schemas.android.com/apk/res/android android:layout_width=match_parent android:layout_height=match_parent android:paddingBottom=@dimen/activity_vertical_margin android:paddingLeft=@dimen/activity_horizontal_margin android:paddingRight=@dimen/activity_horizontal_margin android:paddingTop=@dimen/activity_vertical_margin xmlns:custom=http://schemas.android.com/apk/res-auto android:background=#ffffff > <!--自定义控件,继承RelativeLayout--> <qdong.com.mylibrary.CircularLoadingView android:id=@+id/simple custom:base_color=@color/pager_bg custom:min=0 custom:max=100 custom:argQuantity=100 custom:ratio=0.6 custom:progress_color=@android:color/holo_green_light custom:progress_icon=@mipmap/ic_launcher custom:duration=1000 custom:progress_stroke_width=28 android:layout_centerInParent=true android:layout_width=200dp android:layout_height=200dp> <RelativeLayout android:layout_centerInParent=true android:layout_width=match_parent android:layout_height=match_parent> <TextView android:layout_centerInParent=true android:textSize=20sp android:layout_centerHorizontal=true android:id=@+id/number android:text=0 android:gravity=center android:textColor=@color/pager_bg android:layout_width=wrap_content android:layout_height=wrap_content /> </RelativeLayout> </qdong.com.mylibrary.CircularLoadingView> <Button android:layout_width=wrap_content android:layout_height=wrap_content android:text=Set_Value android:id=@+id/button android:layout_alignParentBottom=true android:layout_alignParentStart=true/> <Button android:layout_width=wrap_content android:layout_height=wrap_content android:text=Animation android:id=@+id/button3 android:layout_alignTop=@+id/button android:layout_alignParentEnd=true/> </RelativeLayout>
MainActivity:
findViewById(R.id.button).setonClickListener(new View.OnClickListener() { @Override public void onClick(View view) { try { mDashedCircularProgress.setValue(66);//没有动画的,直接设置 } catch (Exception e) { e.printstacktrace(); } } }); findViewById(R.id.button3).setonClickListener(new View.OnClickListener() { @Override public void onClick(View view) { try { mDashedCircularProgress.setValue(0);//无动画,归零 mDashedCircularProgress.setValueWithAnimation(100,2000);//带动画 } catch (Exception e) { e.printstacktrace(); } } });
Github地址:https://github.com/506954774/AndroidCircularLoadingView