android自定义view之圆形进度条

changhaiSmile changhaiSmile     2022-11-16     302

关键词:

这段时间正在学习自定义View以及属性动画的知识,然后刚好用这个来练练手,无图无真相,直接看图:


简单自定义了一个比较通用的圆形进度条,像上图所示的可以定义圆的半径,进度颜色,宽度,中间字体等信息。下面我就一步一步来为大家讲解:

1、首先我们先要找出有哪些属性需要自定义的,进度条颜色、进度颜色、整个进度条的半径、进度的宽度、进度条内文字颜色及大小、最大进度、当前进度,后来我加了一个方向的属性,方向表示进度从哪里开始(默认有四个方向,上左下右),确定好之后我们就在attrs中定义出来:

 <declare-styleable name="CustomCircleProgressBar">
    <attr name="outside_color" format="color" />
    <attr name="outside_radius" format="dimension" />
    <attr name="inside_color" format="color" />
    <attr name="progress_text_color" format="color" />
    <attr name="progress_text_size" format="dimension" />
    <attr name="progress_width" format="dimension" />
    <attr name="max_progress" format="integer" />
    <attr name="progress" format="float" />
    <attr name="direction">
        <enum name="left" value="0" />
        <enum name="top" value="1" />
        <enum name="right" value="2" />
        <enum name="bottom" value="3" />
    </attr>
</declare-styleable>

2、然后在自定义View的构造方法中获取一下这些值:

public CustomCircleProgressBar(Context context, AttributeSet attrs, int defStyleAttr) 
    super(context, attrs, defStyleAttr);
    TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomCircleProgressBar, defStyleAttr, 0);
    outsideColor = a.getColor(R.styleable.CustomCircleProgressBar_outside_color, ContextCompat.getColor(getContext(), R.color.colorPrimary));
    outsideRadius = a.getDimension(R.styleable.CustomCircleProgressBar_outside_radius, DimenUtil.dp2px(getContext(), 60.0f));
    insideColor = a.getColor(R.styleable.CustomCircleProgressBar_inside_color, ContextCompat.getColor(getContext(), R.color.inside_color));
    progressTextColor = a.getColor(R.styleable.CustomCircleProgressBar_progress_text_color, ContextCompat.getColor(getContext(), R.color.colorPrimary));
    progressTextSize = a.getDimension(R.styleable.CustomCircleProgressBar_progress_text_size, DimenUtil.dp2px(getContext(), 14.0f));
    progressWidth = a.getDimension(R.styleable.CustomCircleProgressBar_progress_width, DimenUtil.dp2px(getContext(), 10.0f));
    progress = a.getFloat(R.styleable.CustomCircleProgressBar_progress, 50.0f);
    maxProgress = a.getInt(R.styleable.CustomCircleProgressBar_max_progress, 100);
    direction = a.getInt(R.styleable.CustomCircleProgressBar_direction, 3);

    a.recycle();

    paint = new Paint();

3、接下来我们要重写onMeasure方法,让其可以自适应你的设置:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
    int width;
    int height;
    int size = MeasureSpec.getSize(widthMeasureSpec);
    int mode = MeasureSpec.getMode(widthMeasureSpec);

    if (mode == MeasureSpec.EXACTLY) 
        width = size;
     else 
        width = (int) ((2 * outsideRadius) + progressWidth);
    
    size = MeasureSpec.getSize(heightMeasureSpec);
    mode = MeasureSpec.getMode(heightMeasureSpec);
    if (mode == MeasureSpec.EXACTLY) 
        height = size;
     else 
        height = (int) ((2 * outsideRadius) + progressWidth);
    
    setMeasuredDimension(width, height);

4、这两块就不多说了,相信大多数看官应该都知道,接下来我们来分析要画些什么?怎么画?首先肯定是画最底层的那个圆环了,给画笔设置空心属性,然后设置线的宽度,就可以画一个圆环了:

@Override
protected void onDraw(Canvas canvas) 
    super.onDraw(canvas);
    int circlePoint = getWidth() / 2;
    //第一步:画背景(即内层圆)
    paint.setColor(insideColor); //设置圆的颜色
    paint.setStyle(STROKE); //设置空心
    paint.setStrokeWidth(progressWidth); //设置圆的宽度
    paint.setAntiAlias(true);  //消除锯齿
    canvas.drawCircle(circlePoint, circlePoint, outsideRadius, paint); //画出圆


5、然后我们接着画外面的进度,外面进度就是一段弧,根据我们获取的进度和总进度来画这段弧,画弧需要用到canvas.drawArc()这个方法,这个方法有两个重载方法:

drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,Paint paint)
drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle,boolean useCenter,Paint paint)
其实,可以看作是一个方法,因为RectF这个东西呢就是由left top right bottom构成的,那RectF这玩意儿到底什么东西呢,我也不知道,那就去看源码呗:

RectF holds four float coordinates for a rectangle.
The rectangle isrepresented by the coordinates of its 4 edges (left, top, right bottom).
These fields can be accessed directly. 
Use width() and height() to retrieve the rectangle's width and height. 
Note: most methods do not check to see that the coordinates are sorted correctly (i.e. left <= right and top <= bottom).

简单点说,这东西可以构造一个矩形,如何构造呢?我也不知道,哈哈哈!我们不妨来看看它的构造方法:

@param left   The X coordinate of the left side of the rectangle
@param top    The Y coordinate of the top of the rectangle
@param right  The X coordinate of the right side of the rectangle
@param bottom The Y coordinate of the bottom of the rectangle

这里解释一下,矩形有四个点,这四个值就把矩形四个点的坐标给确定了,left表示矩形左边两个点的X轴坐标,right表示矩形右边两个点的X轴坐标,top表示矩形上边两个点的Y轴坐标,bottom表示矩形下边两个点的Y轴坐标,详细参照下图:


RectF的左上右下
写法一:
RectF oval = new RectF(circlePoint - outsideRadius, circlePoint - outsideRadius, circlePoint + outsideRadius, circlePoint + outsideRadius);  //用于定义的圆弧的形状和大小的界限
写法二:
RectF oval = new RectF();
oval.left=circlePoint - outsideRadius;
oval.top=circlePoint - outsideRadius;
oval.right=circlePoint + outsideRadius;
oval.bottom=circlePoint + outsideRadius;

然后drawArc()方法中的第二(五)个参数startAngle表示我们画弧度开始的角度,这里的值为0-3600表示三点钟方向,90表示六点钟方向,以此类推;后面那个参数sweepAngle表示要画多少弧度,这里的取值也是从0-360,我们通常使用当前进度占总进度的百分之多少,再乘以弧度360就是我们所要画的弧度了;再后面那个参数useCenter表示是否连接圆心一起画,下面来看看代码:

//第二步:画进度(圆弧)不连接圆心
paint.setColor(outsideColor);  //设置进度的颜色
RectF oval = new RectF(circlePoint - outsideRadius, circlePoint - outsideRadius, circlePoint + outsideRadius, circlePoint + outsideRadius);  //用于定义的圆弧的形状和大小的界限
canvas.drawArc(oval, CustomCircleProgressBar.DirectionEnum.getDegree(direction), 360 * (progress / maxProgress), false, paint);  //根据进度画圆弧

不连接圆心
//第二步:画进度(圆弧)连接圆心
paint.setColor(outsideColor);  //设置进度的颜色
RectF oval = new RectF(circlePoint - outsideRadius, circlePoint - outsideRadius, circlePoint + outsideRadius, circlePoint + outsideRadius);  //用于定义的圆弧的形状和大小的界限
canvas.drawArc(oval, CustomCircleProgressBar.DirectionEnum.getDegree(direction), 360 * (progress / maxProgress), true, paint);  //根据进度画圆弧

连接圆心

6、接下来就是画圆环内的百分比文字了,可能有的人就说,画文字嘛,那不是很简单,直接drawText方法画不就好了,要什么值就传什么呗!大兄弟别急撒,下面给大家看看直接画文字的效果:

paint.setColor(progressTextColor);
paint.setTextSize(progressTextSize);
paint.setStrokeWidth(0);
progressText = (int) ((progress / maxProgress) * 100) + "%";
canvas.drawText(progressText, circlePoint , circlePoint, paint);

WTF,发生了什么???所以说大兄弟,憋着急,这里drawText方法是从文字的左上角开始画的,所以我们需要剪去文字一半的宽高:

rect = new Rect();
paint.getTextBounds(progressText, 0, progressText.length(), rect);
canvas.drawText(progressText, circlePoint- rect.width() / 2 , circlePoint- rect.height() / 2, paint);

再次WTF,可能又有人说,LZ你骗人,它还是没有居中,这尼玛心中顿时有一万只草泥马在奔腾,别急,还没讲完,给你看看源码你就知道了:

/**
 * Draw the text, with origin at (x,y), using the specified paint. The
 * origin is interpreted based on the Align setting in the paint.
 *
 * @param text  The text to be drawn
 * @param x     The x-coordinate of the origin of the text being drawn
 * @param y     The y-coordinate of the baseline of the text being drawn
 * @param paint The paint used for the text (e.g. color, size, style)
 */
public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) 
    native_drawText(mNativeCanvasWrapper, text, 0, text.length(), x, y, paint.mBidiFlags,
            paint.getNativeInstance(), paint.mNativeTypeface);

不知道各位有没有看到第三个参数y的解释,它不是纵轴的坐标,而是基准线y坐标,至于这个基准线,LZ不打算在这里展开讲,因为这个也有很多内容,给大家推荐一篇讲的非常详细的博客:
自定义控件之绘图篇( 五):drawText()详解
接下来来看看咱是怎么写的:

//第三步:画圆环内百分比文字
rect = new Rect();
paint.setColor(progressTextColor);
paint.setTextSize(progressTextSize);
paint.setStrokeWidth(0);
progressText = getProgressText();
paint.getTextBounds(progressText, 0, progressText.length(), rect);
Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt();
int baseline = (getMeasuredHeight() - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;  //获得文字的基准线
canvas.drawText(progressText, getMeasuredWidth() / 2 - rect.width() / 2, baseline, paint);

再来看看最终的效果:


好了,现在进度和文字都画出来了,个人觉得就这样直接展示在用户眼前显得有点生硬,有没有什么办法让它的进度从零开始跑动画到我们要设置的进度值呢,答案是肯定的咯,这里我们可以用属性动画来实现,前面几篇博客我们有讲到属性动画的知识,如果你还没有看过的话,请移步:

Android自定义view之属性动画熟悉

Android自定义view之属性动画初见

这里我们使用的是ValueAnimator,通过监听动画改变进度的值来设置圆环的进度:

private void startAnim(float startProgress) 
    animator = ObjectAnimator.ofFloat(0, startProgress);
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() 
        @Override
        public void onAnimationUpdate(ValueAnimator animation) 
            CustomCircleProgressBar.this.progress = (float) animation.getAnimatedValue();
            postInvalidate();
        
    );
    animator.setStartDelay(500);   //设置延迟开始
    animator.setDuration(2000);
    animator.setInterpolator(new LinearInterpolator());   //动画匀速
    animator.start();

到此就完成了自定义的原型进度条了。源码已上传至Github,有需要的同学可以下载下来看看,欢迎Star,Fork


android之自定义圆形进度条

    在Android开发中,对于进度条想必大家不会陌生。例如,应用在执行一个耗时操作时,会通过展示一个进度条来显示“加载中...”的动画作为友好页面以提高用户体验。对于这样的进度条,最简单的实现方式... 查看详情

compose自定义条形进度条(代码片段)

前言Compose自定义View其实比View系统更方便简单,比如接下来本文要介绍的就是使用Compose实现View系统中常见的条形进度条。自定义进度条Composematerial包中提供了CircularProgressIndicator实现View系统中的圆形进度条,因为Compose没... 查看详情

compose自定义条形进度条(代码片段)

前言Compose自定义View其实比View系统更方便简单,比如接下来本文要介绍的就是使用Compose实现View系统中常见的条形进度条。自定义进度条Composematerial包中提供了CircularProgressIndicator实现View系统中的圆形进度条,因为Compose没... 查看详情

ios制作个圆形进度条

参考技术A1.需要做个这样的圆形进度条 自定义弹窗view就不说了,主要是这个圆形进度view,底色是灰色然后有进度灰色被黑色覆盖或者说变成黑色首先我们自定一个view,加个uilabel做进度lbael@property(nonatomic,strong)UILabel*percentLabel;/... 查看详情

android自定义控件篇圆形进度条(代码片段)

一、效果图二、代码逻辑/***funcation:圆形进度条控件*/publicclassCircleProgressViewextendsViewprivatePaintmBackPaint,mProgPaint;//绘制画笔privateRectFmRectF;//绘制区域privateint[]mColorArray;//圆环渐变色privateintmProgress;//圆环进度(0-100)publicCircleProgressView(Co... 查看详情

android开发之制作圆形头像自定义view,直接引用工具类,加快开发速度。带有源代码学习

作者:程序员小冰Android开发之制作圆形头像自定义View,直接引用工具类,加快开发速度。带有源代码学习大家都知道。现在好多头像都是圆形的,不再是以前的正方形或者长方形。因此今天给大家带来的就是如何制作一个圆形头... 查看详情

自定义view学习之12/7(进度条之混合模式)

今天重点内容是我们学习自定义view里面的混合模式,其实我们的画布就跟photoshop一样,是个图层关系,一层盖着一层,这样就导致有很多种覆盖模式,这就是我们今天的主题,“混合模式”。好,现在... 查看详情

android进阶之自定义view(文字圆形边框)(代码片段)

最近写自定义的view写了很多,打算好好完整学习一下,顺便也是记录下,首先来看看效果大概是实现上面的效果吧,其实做起来很简单,只需要继承TextView,然后在外面画上一个框即可,代码如下:public... 查看详情

自定义view实现钟摆效果进度条pendulumview

...第一步,自定义属性自定义属性  建立属性文件  在Android项目的res->values目录下 查看详情

如何使用逆时针动画显示自定义圆形进度条?

】如何使用逆时针动画显示自定义圆形进度条?【英文标题】:HowtodisplaycustomcircularprogressbarwithAnti-Clockwiseanimation?【发布时间】:2014-07-0313:54:48【问题描述】:我正在使用与测验相关的应用程序。此处将显示自定义圆形进度条,... 查看详情

android自定义view之draw过程(上)

参考技术ADraw过程系列文章Android展示之三部曲:前边我们已经分析了:这俩最主要的任务是:确定View/ViewGroup可绘制的矩形区域。接下来将会分析,如何在这给定的区域内绘制想要的图形。通过本篇文章,你将了解到:Android提供... 查看详情

带有实心圆圈的自定义圆形进度条

】带有实心圆圈的自定义圆形进度条【英文标题】:CustomCircularProgressBarwithsolidcircles【发布时间】:2015-05-1505:52:21【问题描述】:我想要一个这样的圆形进度条我试过了,但它看起来不像圆形以及如何将动画与淡入淡出一起放置... 查看详情

一起talkandroid吧(第四百九十六回:自定义view实例二:环形进度条)

...是"如何使用Java版MQTT客户端",这一回中咱们说的例子是"自定义View实例二:环形进度条"。闲话休提,言归正转,让我们一起TalkAndroid吧!知识回顾看官们,我们又回到了自定义View相关的知识中,在这里对旧知识做一些回顾:首先... 查看详情

compose自定义条形进度条(代码片段)

前言Compose自定义View其实比View系统更方便简单,比如接下来本文要介绍的就是使用Compose实现View系统中常见的条形进度条。自定义进度条Composematerial包中提供了CircularProgressIndicator实现View系统中的圆形进度条,因为Compose没... 查看详情

如何更改圆形进度条的颜色?

...gressbar?【发布时间】:2011-07-1707:03:54【问题描述】:我在Android上使用循环进度条。我想改变它的颜色。我正在使用"?android:attr/progressBarStyleLargeInverse"风格。那么如何改变进度条的颜色呢。如何自定义样式?另外,风格的定义是什... 查看详情

C# 自定义控件(圆形进度条) Xamarin Forms

】C#自定义控件(圆形进度条)XamarinForms【英文标题】:C#customcontrol(circleprogressbar)XamarinForms【发布时间】:2016-09-1916:15:35【问题描述】:我想知道用XamarinForms创建与此图像类似的东西的最佳方法:我从来没有创造过这样的东西。... 查看详情

首页2--动态自定义圆形进度条

A.绘制圆环,圆弧,文本//1.画圆环//原点坐标floatcircleX=width/2;floatcircleY=width/2;//半径floatradius=width/2-roundWidth/2;//设置画笔的属性paint.setColor(roundColor);paint.setStrokeWidth(roundWidth);paint.setStyle(Paint.Style. 查看详情

composecanvas自定义圆形进度条(代码片段)

@ComposablefunCircleRing(boxWidthDp:Int,viewModel:TaskViewModel)Canvas(modifier=Modifier.size(boxWidthDp.dp),onDraw=valstrokWidth=30F//灰色背景drawArc(Color(0,0,0,15),startAngle=160f,s 查看详情