Android绘制圆形进度条

一、背景介绍

我们在项目中,经常会见到圆形进度条,看起来很美观、直观。刚好最近项目中有这样的需求,记录一下,顺便回顾下自定义View的知识。

二、实现思路

自定义View,就是在画布中绘制View,需要重写onDraw方法。该View可以拆分成以下几部分:

1)需要画一个浅绿色的圆

2)需要画一个白色的圆

3)圆圈中有进度数字的显示

4)圆圈中可以自定义顶部和底部不同文案的提示

三、主要方法介绍

1、drawArc:由上图可以看出,该圆需要画出圆弧表示进度,所以选择drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)方法。

1)参数:

oval-用于确定圆弧形状与尺寸的椭圆边界(即椭圆外切矩形)

startAngle-开始角度(以时钟3点钟为0°,逆时针为正方向)

sweepAngle-旋转角度(以时钟3点钟为0°,逆时针为正方向)

useCenter-是否包含圆心

paint-画笔

2)绘制原理

  • 当RectF(float left,float top,float right,float bottom)中right-left等于bottom-top时(长=宽),这时画出的就是个圆。

  • 以矩形中心为圆心,以3点钟方向为0°,逆时针为正方向,从0°旋转startAngle度,和椭圆相交得到一条直线和一个焦点。

  • 从这条直线开始,正方向旋转sweepAngle度,得到另一条直线和焦点,这样就可以得到两个焦点间的圆弧。

2、drawText(String text, float x, float y, Paint paint):文本的绘制方法。

参数:

text-文本

x-该文本的左边与屏幕左边的距离

y-该文本baseline在屏幕上的位置

paint-画笔

需要注意的是,参数y不是表示竖直方向上的位置,而是该文本baseline在屏幕上的位置。

根据官方API说明,Paint的TextAlign属性决定了text相对于起始坐标x的相对位置。默认left,文本从x的右边开始绘制,如果是center,则x坐标在文本的中间。

baseline的介绍参考:http://www.xyczero.com/blog/article/20/

四、画圆

1)画一个背景圆

        //1-圆弧的位置:整圆,再绘制进度圆弧
        mArcCirclePaint.setColor(mCircleBackgroundColor);
        mArcCirclePaint.setStrokeWidth(mStrokeWidth);
        //屏幕宽度
        int width = getMeasuredWidth();
        RectF rectF = new RectF();
        rectF.left = (width-mWidth)/2;//左上角X
        rectF.top = mWidth*0.1f;//左上角Y
        rectF.right = (width-mWidth)/2+mWidth;//右上角X
        rectF.bottom = mWidth*0.9f;//右上角Y
        if ((rectF.right - rectF.left) > (rectF.bottom- rectF.top)){//正方形矩形,保证画出的圆不会变成椭圆
            float space = (rectF.right - rectF.left) - (rectF.bottom- rectF.top);
            rectF.left += space/2;
            rectF.right -= space/2;
        }
        canvas.drawArc(rectF,270,360,false,mArcCirclePaint);//第2个参数:时钟3点处为0度,逆时针为正方向

2)画进度圆

使用同一个Paint,改变其颜色,在画布上绘制一样大小的圆,只是旋转角度值不一样。

 mArcCirclePaint.setColor(mProgressColor);
 //设置边角为圆
 mArcCirclePaint.setStrokeCap(Paint.Cap.ROUND);
 mArcCirclePaint.setStrokeWidth(mInnerStrokeWidth);
 canvas.drawArc(rectF,270,mAngleValue,false,mArcCirclePaint);

3)绘制文本

不同文本只是位置不一样,计算好位置就可以绘制出文本了。

        //2-文本的位置:居中显示
        int centerX = width/2;
        //计算文本宽度
        int textWidth = (int) mTextPaint.measureText(mText, 0, mText.length());
        //计算baseline:垂直方向居中
        Paint.FontMetricsInt fontMetrics = mTextPaint.getFontMetricsInt();
        int baseline = (getMeasuredHeight() - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;
        int textX = centerX-textWidth/2;
        mTextPaint.setColor(mTextColor);
        mTextPaint.setTextSize(mTextSize);
        canvas.drawText(mText,textX,baseline,mTextPaint);

        if (mTopText != null && !mTopText.equals("")) {
            textWidth = (int) mTextPaint.measureText(mTopText, 0, mTopText.length());
            textX = centerX - textWidth / 2;
            mTextPaint.setTextSize(mTopTextSize);
            mTextPaint.setColor(mTopTextColor);
            canvas.drawText(mTopText, textX, baseline - 20, mTextPaint);
        }

        if (mBottomText != null && !mBottomText.equals("")) {
            textWidth = (int) mTextPaint.measureText(mBottomText, 0, mBottomText.length());
            textX = centerX - textWidth / 2;
//            mTextPaint.reset();
//            mTextPaint.setAntiAlias(true);
//            mTextPaint.setLinearText(true);
            mTextPaint.setTextSize(mTopTextSize);
            mTextPaint.setColor(mBottomTextColor);
            canvas.drawText(mBottomText, textX, baseline + 20, mTextPaint);
        }

五、总结

其实很多的自定义View都是在画布canvas中画出来的,看着复杂(其实难在位置的计算),但是只要将其拆分成几部分,一一画出再组合就好了。

附上源码:工程demo

package com.example.ViewDemo;

import android.content.Context;
import android.graphics.*;
import android.util.AttributeSet;
import android.view.View;

/**
 * 自定义View方式三:重新绘制,继承View
 * 第一步:画出外圆drawArc
 * 第二步:画出进度圆drawArc
 * 第三步:画出文本:中间文本,顶部文本,底部文本drawText
 * Created by cjy on 17/6/14.
 */
public class ArcCircleView extends View {
    private Context mContext;
    /**
     * 文本画笔
     */
    private Paint mTextPaint;
    /**
     * 圆弧画笔
     */
    private Paint mArcCirclePaint;
    /**
     * 宽度
     */
    private float mWidth = 100.0f;
    /**
     * 文本
     */
    private String mText  ="0%";
    /**
     * 底部文本
     */
    private String mTopText  ="";
    /**
     * 底部文本
     */
    private String mBottomText  ="";
    /**
     * 弧度
     */
    private int mAngleValue = 0;
    /**
     * 圆的背景色:默认浅绿色
     */
    private int mCircleBackgroundColor = 0x4c11af9c;
    /**
     * 进度的颜色,默认白色
     */
    private int mProgressColor = 0xffffffff;
    /**
     * 顶部文本的颜色,默认白色
     */
    private int mTopTextColor = 0xffffffff;
    /**
     * 底部文本的颜色,默认白色
     */
    private int mBottomTextColor = 0xffffffff;
    /**
     * 文本的颜色,默认白色
     */
    private int mTextColor = 0xffffffff;
    /**
     * 边宽
     */
    private int mStrokeWidth = 8;
    /**
     * 进度圆边宽
     */
    private int mInnerStrokeWidth = 7;
    /**
     * 文本大小
     */
    private int mTextSize = 12;
    /**
     * 顶部文本大小
     */
    private int mTopTextSize = 10;

    public ArcCircleView(Context context) {
        super(context);
        init(context);
    }

    public ArcCircleView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public ArcCircleView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context);
    }

    private void init(Context context){
        mContext = context;

        mTextPaint  = new Paint();
        //设置抗锯齿
        mTextPaint.setAntiAlias(true);
        //使文本看起来更清晰
        mTextPaint.setLinearText(true);

        mArcCirclePaint  = new Paint();
        mArcCirclePaint.setAntiAlias(true);
        mArcCirclePaint.setStyle(Paint.Style.STROKE);

    }

    public void setWidth(float width){
        mWidth = width;
        invalidate();
    }

    public void setText(String text){
        mText = text;
        invalidate();
    }

    public void setTopText(String text){
        mTopText = text;
        invalidate();
    }

    public void setBottomText(String text){
        mBottomText = text;
        invalidate();
    }

    public void setTextColor(int textColor) {
        this.mTextColor = mContext.getResources().getColor(textColor);
        invalidate();
    }

    public void setBottomTextColor(int bottomTextColor) {
        this.mBottomTextColor = mContext.getResources().getColor(bottomTextColor);
        invalidate();
    }

    public void setTopTextColor(int topTextColor) {
        this.mTopTextColor = mContext.getResources().getColor(topTextColor);
        invalidate();
    }

    public void setProgressColor(int progressColor) {
        this.mProgressColor = mContext.getResources().getColor(progressColor);
        invalidate();
    }

    public void setCircleBackgroundColor(int circleBackgroundColor) {
        this.mCircleBackgroundColor = mContext.getResources().getColor(circleBackgroundColor);
        invalidate();
    }

    public void setStrokeWidth(int strokeWidth){
        this.mStrokeWidth = strokeWidth;
        invalidate();
    }

    public void setInnerStrokeWidth(int innerStrokeWidth){
        this.mInnerStrokeWidth = innerStrokeWidth;
        invalidate();
    }

    public void setTextSize(int textSize){
        this.mTextSize = textSize;
        invalidate();
    }

    public void setTopTextSize(int topTextSize){
        this.mTopTextSize = topTextSize;
        invalidate();
    }

    /**
     * 设置进度
     * @param progress
     */
    public void setProgress(float progress){
        int angleValue = (int) ((progress * 1.0)/100 * 360);
        if (angleValue != 0 && progress <= 100){
            mAngleValue  = angleValue;
            mText = String.valueOf(progress)+"%";
        }
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //1-圆弧的位置:整圆,再绘制进度圆弧
        mArcCirclePaint.setColor(mCircleBackgroundColor);
        mArcCirclePaint.setStrokeWidth(mStrokeWidth);
        //屏幕宽度
        int width = getMeasuredWidth();
        RectF rectF = new RectF();
        rectF.left = (width-mWidth)/2;//左上角X
        rectF.top = mWidth*0.1f;//左上角Y
        rectF.right = (width-mWidth)/2+mWidth;//右上角X
        rectF.bottom = mWidth*0.9f;//右上角Y
        if ((rectF.right - rectF.left) > (rectF.bottom- rectF.top)){//正方形矩形,保证画出的圆不会变成椭圆
            float space = (rectF.right - rectF.left) - (rectF.bottom- rectF.top);
            rectF.left += space/2;
            rectF.right -= space/2;
        }
        canvas.drawArc(rectF,270,360,false,mArcCirclePaint);//第2个参数:时钟3点处为0度,逆时针为正方向

        mArcCirclePaint.setColor(mProgressColor);
        //设置边角为圆
        mArcCirclePaint.setStrokeCap(Paint.Cap.ROUND);
        mArcCirclePaint.setStrokeWidth(mInnerStrokeWidth);
        canvas.drawArc(rectF,270,mAngleValue,false,mArcCirclePaint);

        //2-文本的位置:居中显示
        int centerX = width/2;
        //计算文本宽度
        int textWidth = (int) mTextPaint.measureText(mText, 0, mText.length());
        //计算baseline:垂直方向居中
        Paint.FontMetricsInt fontMetrics = mTextPaint.getFontMetricsInt();
        int baseline = (getMeasuredHeight() - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;
        int textX = centerX-textWidth/2;
        mTextPaint.setColor(mTextColor);
        mTextPaint.setTextSize(mTextSize);
        canvas.drawText(mText,textX,baseline,mTextPaint);

        if (mTopText != null && !mTopText.equals("")) {
            textWidth = (int) mTextPaint.measureText(mTopText, 0, mTopText.length());
            textX = centerX - textWidth / 2;
            mTextPaint.setTextSize(mTopTextSize);
            mTextPaint.setColor(mTopTextColor);
            canvas.drawText(mTopText, textX, baseline - 20, mTextPaint);
        }

        if (mBottomText != null && !mBottomText.equals("")) {
            textWidth = (int) mTextPaint.measureText(mBottomText, 0, mBottomText.length());
            textX = centerX - textWidth / 2;
//            mTextPaint.reset();
//            mTextPaint.setAntiAlias(true);
//            mTextPaint.setLinearText(true);
            mTextPaint.setTextSize(mTopTextSize);
            mTextPaint.setColor(mBottomTextColor);
            canvas.drawText(mBottomText, textX, baseline + 20, mTextPaint);
        }
    }

}

  

时间: 2024-10-04 23:52:45

Android绘制圆形进度条的相关文章

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

自定义圆形进度条效果图如下:应用场景如动态显示分数等. view的自定义属性如下attr.xml <?xml version="1.0" encoding="UTF-8"?> <resources> <declare-styleable name="ArcProgressbar">         <!-- 圆环起始角度-->         <attr name="startAng

canvas 绘制圆形进度条

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>canvas 圆形进度条效果</title> <style> *{margin:0;padding:0;} body{text-align:center;background-color:#000;} </style> </he

Android自定义圆形进度条

首先看一下效果: 自定义的View: import com.example.circlepregress.R; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.RectF

Android View 之进度条+拖动条+星级评论条....

PS:将来的你会感谢现在奋斗的自己.... 学习内容: 1.进度条 2.拖动条 3.星级评论条 1.进度条...       进图条这东西想必大家是很熟悉的...为了使用户不会觉得应用程序死掉了,因此为之设置一个进度条使应用程序的运行状态更好的反馈给客户...这也就是进度条的作用...因此一般的应用程序都会加入进度条...进度条分为圆形进度条和线性的进度条...目的都是一样的,只是展示的效果是不同的...用代码讲解一下... <LinearLayout xmlns:android="htt

HTML5 canvas带渐变色的圆形进度条动画

query-circle-progress是一款带渐变色的圆形进度条动画特效jQuery插件.该圆形进度条使用的是HTML5 canvas来绘制圆形进度条及其动画效果,进度条使用渐变色来填充,效果非常的酷. 效果演示:http://www.htmleaf.com/Demo/201505271919.html 下载地址:http://www.htmleaf.com/html5/html5-canvas/201505271918.html

Android自定义View——圆形进度条式按钮

介绍 今天上班的时候有个哥们问我怎么去实现一个按钮式的进度条,先来看看他需要实现的效果图. 和普通的圆形进度条类似,只是中间的地方有两个状态表示,未开始,暂停状态.而且他说圆形进度的功能已经实现了.那么我们只需要对中间的两个状态做处理就行了. 先来看看实现的效果图: 上面说了我们只需要处理中间状态的变化就可以了,对于进度的处理直接使用了弘洋文章中实现: http://blog.csdn.net/lmj623565791/article/details/43371299 下面开始具体实现. 具体实

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

转载著名出处 : http://blog.csdn.net/shulianghan/article/details/40351487 代码下载 : -- CSDN 下载地址 : http://download.csdn.net/detail/han1202012/8069497 ; -- GitHub 地址 : https://github.com/han1202012/CircleProcess.git ; -- 工程示例 : 一. 相关知识点解析 1. 自定义 View 组件构造方法 构造方

Android自定义控件系列之应用篇——圆形进度条

一.概述 在上一篇博文中,我们给大家介绍了Android自定义控件系列的基础篇.链接:http://www.cnblogs.com/jerehedu/p/4360066.html 这一篇博文中,我们将在基础篇的基础上,再通过重写ondraw()方法和自定义属性实现圆形进度条,效果如图所示: 二.实现步骤   1.  编写自定义组件MyCircleProgress扩展View public class MyCircleProgress extends View { - } 2.  在MyCircl

Android 高手进阶之自定义View,自定义属性(带进度的圆形进度条)

转载请注明地址:http://blog.csdn.net/xiaanming/article/details/10298163 很多的时候,系统自带的View满足不了我们功能的需求,那么我们就需要自己来自定义一个能满足我们需求的View,自定义View我们需要先继承View,添加类的构造方法,重写父类View的一些方法,例如onDraw,为了我们自定义的View在一个项目中能够重用,有时候我们需要自定义其属性,举个很简单的例子,我在项目中的多个界面使用我自定义的View,每个界面该自定义View