android 圆形进度条的简单实现

在网上看了些进度条效果,于是就想写关于这方面博客,先看下今天要实现一些进度条的效果图:


当然要实现这些效果是基于你对canvas类绘制一些图形是了解的,否则估计也不好看懂,我尽量讲的详细点,

先从简单的画圆开始一步步把这个效果实现出来,这样几乎每个人都能看的懂,

画圆的代码:

package com.example.pbdemo;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
/**  * Created by admin on 2016/7/5.  */ public class MyProgressView extends View {
    private Paint mPaint;
    public MyProgressView(Context context) {
        this(context,null);
    }
    public MyProgressView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }
    public MyProgressView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mPaint = new Paint();
        mPaint.setColor(Color.BLUE);//设置画笔的颜色
        mPaint.setStrokeWidth(8);//设置画笔的宽度
        mPaint.setAntiAlias(true);//设置看锯齿
        mPaint.setStyle(Paint.Style.STROKE);//设置成空心
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawCircle(100,100,80,mPaint);
    }
}
效果:


现在画个弧,示意图:



public class MyProgressView extends View {
    private Paint mPaint;
    public MyProgressView(Context context) {
        this(context,null);
    }
    public MyProgressView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }
    public MyProgressView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mPaint = new Paint();
        mPaint.setColor(Color.BLUE);//设置画笔的颜色
        mPaint.setStrokeWidth(8);//设置画笔的宽度
        mPaint.setAntiAlias(true);//设置看锯齿
        mPaint.setStyle(Paint.Style.STROKE);//设置成空心
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawCircle(100,100,80,mPaint);
        RectF rectF = new RectF(20,20,180,180);
        mPaint.setColor(Color.YELLOW);
        canvas.drawArc(rectF,0,30,false,mPaint);
    }
}
效果图:

android 圆形进度条的简单实现_第1张图片

ok,现在就差让弧度进行动态的改变,这样就达到黄色所表示的弧度是动态的,这就有个简单的计算,比如进度条最大值是100,刚开始是0,每过1秒后+1,同时去刷新界面,那么弧度怎么算呢,你看当进度条最大值为100时,刚好弧度走一圈,一圈是360度,假如当前进度为10,那么它所表示弧度=10/100*360,那么现在就可以写代码了,根据这个思路

package com.example.pbdemo;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
/**  * Created by admin on 2016/7/5.  */ public class MyProgressView extends View {
    private static final String TAG ="MyProgressView" ;
    private Paint mPaint;//这是画圆所用的画笔
    private Paint anglePaint;//这是画弧的画笔
    private int startingDegree = 0;//开始弧度
    private int max = 100;
    private int progress;
    public MyProgressView(Context context) {
        this(context,null);
    }
    public MyProgressView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }
    public MyProgressView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mPaint = new Paint();
        anglePaint = new Paint();
        anglePaint.setStrokeWidth(8);
        anglePaint.setColor(Color.YELLOW);
        mPaint.setColor(Color.BLUE);//设置画笔的颜色
        mPaint.setStrokeWidth(8);//设置画笔的宽度
        mPaint.setAntiAlias(true);//设置看锯齿
        mPaint.setStyle(Paint.Style.STROKE);//设置成空心
        anglePaint.setStyle(Paint.Style.STROKE);//设置成空心
        anglePaint.setAntiAlias(true);
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawCircle(100,100,80,mPaint);
        RectF rectF = new RectF(20,20,180,180);
        canvas.drawArc(rectF,getStartingDegree(),getProgressAngle(),false,anglePaint);
    }
    /**  * 弧度从哪角度开始  * @return  */  public int getStartingDegree() {
        return startingDegree;
    }

    /**  * 设置进度值,实时更新进度  * @param progress  */  public void setProgress(int progress){
        this.progress = progress;
        if (this.progress > max) {
            this.progress %= max;
        }
        invalidate();
    }
    /**  * 获取当前进度值  * @return  */  public int getProgress(){
        return progress;
    }
    /**  * 弧度所经过的度  * @return  */  private float getProgressAngle() {
        return getProgress() / (float) max * 360f;
    }
}

要100毫秒去更新进度值

public class MainActivity extends AppCompatActivity {
    private Timer timer;
    private MyProgressView myprogressview;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myprogressview = (MyProgressView) findViewById(R.id.myprogressview);
        timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        myprogressview.setProgress(myprogressview.getProgress()+1);
                    }
                });
            }
        }, 1000, 100);
    }
}
效果:


现在添加一个需求就是在圆的中心点显示当前进度值,这个就要计算文字所在的坐标x,y了

分析图:

android 圆形进度条的简单实现_第2张图片

代码如下:

public class MyProgressView extends View {
    private static final String TAG ="MyProgressView" ;
    private Paint mPaint;//这是画圆所用的画笔
    private Paint textPaint;//绘制文字的画笔
    private Paint anglePaint;//这是画弧的画笔
    private int startingDegree = 0;//开始弧度
    private int max = 100;
    private int progress;
    public MyProgressView(Context context) {
        this(context,null);
    }
    public MyProgressView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }
    public MyProgressView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        textPaint = new Paint();
        textPaint.setColor(Color.RED);
        textPaint.setTextSize(24);//设置文字的大小
        textPaint.setAntiAlias(true);
        mPaint = new Paint();
        anglePaint = new Paint();
        anglePaint.setStrokeWidth(8);
        anglePaint.setColor(Color.YELLOW);
        mPaint.setColor(Color.BLUE);//设置画笔的颜色
        mPaint.setStrokeWidth(8);//设置画笔的宽度
        mPaint.setAntiAlias(true);//设置看锯齿
        mPaint.setStyle(Paint.Style.STROKE);//设置成空心
        anglePaint.setStyle(Paint.Style.STROKE);//设置成空心
        anglePaint.setAntiAlias(true);
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawCircle(100,100,80,mPaint);
        RectF rectF = new RectF(20,20,180,180);
        float textHeight = textPaint.descent() + textPaint.ascent();//算出文字的高度
        String text = getProgress()+"%";
        canvas.drawText(text,20+80-textPaint.measureText(text)/2,20+80-textHeight/2,textPaint);
        canvas.drawArc(rectF,getStartingDegree(),getProgressAngle(),false,anglePaint);
    }
    /**  * 弧度从哪角度开始  * @return  */  public int getStartingDegree() {
        return startingDegree;
    }

    /**  * 设置进度值,实时更新进度  * @param progress  */  public void setProgress(int progress){
        this.progress = progress;
        if (this.progress > max) {
            this.progress %= max;
        }
        invalidate();
    }
    /**  * 获取当前进度值  * @return  */  public int getProgress(){
        return progress;
    }
    /**  * 弧度所经过的度  * @return  */  private float getProgressAngle() {
        return getProgress() / (float) max * 360f;
    }
}

MainActivity类中的代码不变,那么在这不贴出来了,现在看效果:


现在实现这个效果;



这里有一个值是定的,就是那个缺省的度数,比如是0.2*360,那么进度要跑的就是0.8*360,

代码如下:

package com.example.pbdemo;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
/**  * Created by admin on 2016/7/5.  */ public class MyProgressView extends View {
    private static final String TAG ="MyProgressView" ;
    private Paint textPaint;//绘制文字的画笔
    private Paint anglePaint;//这是画弧的画笔
    private int max = 100;
    private int progress;
    private float arcAngle = 360*0.8f;//
    public MyProgressView(Context context) {
        this(context,null);
    }
    public MyProgressView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }
    public MyProgressView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPainters();
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        RectF rectF = new RectF(100,100,400,400);
        float startAngle = (float) (90+0.1*360f);
        float sweepAngle =  progress / (float)max * arcAngle;
        canvas.drawArc(rectF,startAngle,sweepAngle,false,anglePaint);
    }

    /**  * 设置进度值,实时更新进度  * @param progress  */  public void setProgress(int progress){
        this.progress = progress;
        if (this.progress > max) {
            this.progress %= max;
        }
        invalidate();
    }
    /**  * 获取当前进度值  * @return  */  public int getProgress(){
        return progress;
    }
    private void initPainters() {
        textPaint = new Paint();
        textPaint.setColor(Color.RED);
        textPaint.setTextSize(24);//设置文字的大小
        textPaint.setAntiAlias(true);
        anglePaint = new Paint();
        anglePaint.setStrokeWidth(8);
        anglePaint.setColor(Color.parseColor("#D2691E"));
        anglePaint.setStyle(Paint.Style.STROKE);//设置成空心
        anglePaint.setAntiAlias(true);
        anglePaint.setStrokeCap(Paint.Cap.ROUND);
    }
}
每秒刷新一次,

myprogressview = (MyProgressView) findViewById(R.id.myprogressview);
timer = new Timer();
timer.schedule(new TimerTask() {
    @Override
    public void run() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                myprogressview.setProgress(myprogressview.getProgress()+1);
            }
        });
    }
}, 1000, 100);
效果:


分析图:


现在只是实现了第一步,我们看着效果图是弧度是有二个颜色的,现在把弧度没扫过的颜色也绘制出来,这个其实很简单,底层是绘制一种颜色,上面是弧度经过的颜色

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    RectF rectF = new RectF(100,100,400,400);
    float startAngle = (float) (90+0.1*360f);
    float sweepAngle =  progress / (float)max * arcAngle;
    anglePaint.setColor(Color.parseColor("#D2691E"));
    canvas.drawArc(rectF, startAngle, arcAngle, false, anglePaint);
    anglePaint.setColor(Color.parseColor("#9ACD32"));
    canvas.drawArc(rectF,startAngle,sweepAngle,false,anglePaint);
}
效果:


现在进一步完善我们想要的效果,需要在圆的中心显示当前进度值,这个和上面讲的进度条中绘制文字是一样的,

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    RectF rectF = new RectF(100,100,400,400);
    float startAngle = (float) (90+0.1*360f);
    float sweepAngle =  progress / (float)max * arcAngle;
    anglePaint.setColor(Color.parseColor("#D2691E"));
    canvas.drawArc(rectF, startAngle, arcAngle, false, anglePaint);
    anglePaint.setColor(Color.parseColor("#9ACD32"));
    canvas.drawArc(rectF,startAngle,sweepAngle,false,anglePaint);

    float textHeight = textPaint.descent() + textPaint.ascent();
    String text = progress+"";
    textPaint.setTextSize(50);
    canvas.drawText(text, 100+150-textPaint.measureText(text)/2, 100+150-textHeight/2, textPaint);
    textPaint.setTextSize(20);
    String suffixText = "%";
    canvas.drawText(suffixText, 100+150+textPaint.measureText(text)/2+12,100+150+textHeight,textPaint);
}
效果:



现在就差绘制下面的文字了,

public class MyProgressView extends View {
    private static final String TAG ="MyProgressView" ;
    private Paint textPaint;//绘制文字的画笔
    private Paint anglePaint;//这是画弧的画笔
    private Paint bottomTextPaint;
    private int max = 100;
    private int progress;
    private float arcAngle = 360*0.8f;//
    public MyProgressView(Context context) {
        this(context,null);
    }
    public MyProgressView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }
    public MyProgressView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPainters();


    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        RectF rectF = new RectF(100,100,400,400);
        float startAngle = (float) (90+0.1*360f);
        float sweepAngle =  progress / (float)max * arcAngle;
        anglePaint.setColor(Color.parseColor("#D2691E"));
        canvas.drawArc(rectF, startAngle, arcAngle, false, anglePaint);
        anglePaint.setColor(Color.parseColor("#9ACD32"));
        canvas.drawArc(rectF,startAngle,sweepAngle,false,anglePaint);

        float textHeight = textPaint.descent() + textPaint.ascent();
        String text = progress+"";
        textPaint.setTextSize(50);
        canvas.drawText(text, 100+150-textPaint.measureText(text)/2, 100+150-textHeight/2, textPaint);
        textPaint.setTextSize(20);
        String suffixText = "%";
        canvas.drawText(suffixText, 100+150+textPaint.measureText(text)/2+12,100+150+textHeight,textPaint);

        //
        String bottomText = "memory";
        float arcBottomHeight = 150 * (float) (1 - Math.cos(36 / 180 * Math.PI));//文字的高度
        canvas.drawText(bottomText,100+150-bottomTextPaint.measureText(bottomText)/2,400-arcBottomHeight,bottomTextPaint);
    }

    /**  * 设置进度值,实时更新进度  * @param progress  */  public void setProgress(int progress){
        this.progress = progress;
        if (this.progress > max) {
            this.progress %= max;
            return;
        }
        invalidate();
    }
    /**  * 获取当前进度值  * @return  */  public int getProgress(){
        return progress;
    }
    private void initPainters() {

        bottomTextPaint = new Paint();
        bottomTextPaint.setColor(Color.BLUE);
        bottomTextPaint.setTextSize(40);
        bottomTextPaint.setStrokeWidth(8);

        textPaint = new Paint();
        textPaint.setColor(Color.RED);
        textPaint.setTextSize(24);//设置文字的大小
        textPaint.setAntiAlias(true);
        anglePaint = new Paint();
        anglePaint.setStrokeWidth(8);
        anglePaint.setColor(Color.parseColor("#D2691E"));
        anglePaint.setStyle(Paint.Style.STROKE);//设置成空心
        anglePaint.setAntiAlias(true);
        anglePaint.setStrokeCap(Paint.Cap.ROUND);
    }
}
效果:


ok,第二个进度条效果算写完了,现在还差最后一个效果如下:


分析图:


代码;

package com.example.pbdemo;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.view.WindowManager;

/**  * Created by admin on 2016/7/5.  */ public class MyProgressView extends View {
    private static final String TAG ="MyProgressView" ;
    private Paint textPaint;//绘制文字的画笔
    private Paint anglePaint;//这是画弧的画笔
    private Paint reanglePaint ;
    private int max = 100;
    private int progress;
    public MyProgressView(Context context) {
        this(context,null);
    }
    public MyProgressView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }
    public MyProgressView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        WindowManager wm = ((MainActivity)getContext()).getWindowManager();
        initPainters();
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        RectF rectF = new RectF(100,100,400,400);
        float yHeight = getProgress()/(float)max*300;
        float angle = (float) (Math.acos((150 - yHeight) / 150) * 180 / Math.PI);
        float startAngle = 90 + angle;//起始弧度
        float sweepAngle = 360 - angle * 2;//所经过的弧度
        canvas.drawArc(rectF, startAngle, sweepAngle, false, anglePaint);
        canvas.save();
        canvas.rotate(180, 250,250);//围绕rectF矩形所在的内切圆的圆心进行旋转
        reanglePaint.setColor(Color.parseColor("#8DB6CD"));
        canvas.drawArc(rectF, 270 - angle, angle * 2, false, reanglePaint);
        canvas.restore();
    }

    /**  * 设置进度值,实时更新进度  * @param progress  */  public void setProgress(int progress){
        this.progress = progress;
        if (this.progress > max) {
            this.progress %= max;
        }
        invalidate();
    }
    /**  * 获取当前进度值  * @return  */  public int getProgress(){
        return progress;
    }
    private void initPainters() {
        textPaint = new Paint();
        textPaint.setTextSize(36);
        textPaint.setAntiAlias(true);
        anglePaint = new Paint();
        anglePaint.setStrokeWidth(8);
        anglePaint.setColor(Color.parseColor("#8B8386"));
        anglePaint.setAntiAlias(true);

        reanglePaint = new Paint();
        reanglePaint.setAntiAlias(true);
    }
}
效果:


效果分析图:

android 圆形进度条的简单实现_第3张图片

现在就差一个显示进度条的值了,这个很简单,唯一要注意的是刚对canvas进行了旋转操作,这些操作是不可逆的,所以要恢复画布,不然绘制文字就倒了

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    RectF rectF = new RectF(100,100,400,400);
    float yHeight = getProgress()/(float)max*300;
    float angle = (float) (Math.acos((150 - yHeight) / 150) * 180 / Math.PI);
    float startAngle = 90 + angle;//起始弧度
    float sweepAngle = 360 - angle * 2;//所经过的弧度
    canvas.drawArc(rectF, startAngle, sweepAngle, false, anglePaint);
    canvas.save();
    canvas.rotate(180, 250,250);//围绕rectF矩形所在的内切圆的圆心进行旋转
    reanglePaint.setColor(Color.parseColor("#8DB6CD"));
    canvas.drawArc(rectF, 270 - angle, angle * 2, false, reanglePaint);
    canvas.restore();

    String text = getProgress()+"%";
    if (!TextUtils.isEmpty(text)) {
        float textHeight = textPaint.descent() + textPaint.ascent();
        canvas.drawText(text, 100+150-textPaint.measureText(text)/2, 100+150-textHeight/2, textPaint);
    }
}
最终效果:


ok,今天任务完成了!


你可能感兴趣的:(android 圆形进度条的简单实现)