zoukankan      html  css  js  c++  java
  • 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);
            }
        }
    
    }
    

      

  • 相关阅读:
    Java 异常处理机制和集合框架
    如何在Windows 下安装Python
    公司为啥要上市?上市对公司有什么好处?
    MongoDB Driver:使用正确的姿势连接复制集
    mongodb复制集开启安全认证
    关于 MongoDB 复制集
    如何高效的使用 Git
    Linux shell常用命令
    MongoDB 查看所有用户账号信息
    MongoDB开启安全认证
  • 原文地址:https://www.cnblogs.com/hacjy/p/7026336.html
Copyright © 2011-2022 走看看