重学 Android 自定义 View 系列(六):环形进度条

Android自定义圆弧进度条(半圆进度条) 圆弧渐变色进度条带指示 圆弧宽高可自由修改 Android 自定义圆弧进度条,支持设置为圆形进度条,弧度圆形渐变色,进度指示条,进度文字。 阅读详情
AI摘要

本文介绍了 Android 自定义环形进度条的实现方法。目标是自定义进度条的最大值、当前进度等信息。通过结构分析明确各部分,给出实现思路,包括定义属性、初始化元素、绘制内容和支持动态更新。还解析了关键技术点,最后给出完整代码和使用示例。

目标

自定义一个环形进度条,可以自定义其最大值、当前进度、背景色、进度色,宽度等信息。

最终效果如下(GIF展示纯色有点问题):
在这里插入图片描述

1. 结构分析


  • 背景圆环:表示进度条的背景。
  • 进度圆环:表示当前进度,根据进度值动态绘制圆环。
  • 进度值文本:在圆环中间展示进度。

2. 实现思路


  1. 定义自定义属性:在 res/values/attrs.xml 中定义自定义属性,以便通过 XML 配置自定义的视图样式。
  2. 初始化视图元素:在构造函数中,根据传入的属性初始化各种画笔、尺寸和视图元素。
  3. 绘制视图内容:重写 onDraw 方法,使用画笔绘制背景圆环、进度圆环和文本。
  4. 支持动态更新进度:提供 setProgress 方法,允许外部动态设置进度,触发视图重绘。

3. 关键技术点解析


我们首先要知道,画圆的基础是有个正方形,或者说有一个正方形的坐标,还要考虑到画笔的宽度,所以我们首先就要确定矩形的大小:

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        int padding = circleWidth / 2;
        rectF.set(padding, padding, w - padding, h - padding);
    }

circleWidth 为圆环的宽,也为圆环画笔的宽。

canvas.drawArc是用于绘制弧形(圆弧)的一种方法,具体参数在前几篇文章已介绍,再次不做赘述。这里主要自定义了进度开始的角度,即 startAngle 属性。

textPaint.setTextAlign(Paint.Align.CENTER)

重点关注下文本画笔的这个属性,这个属性可以保证文本水平居中,而不需要手动去计算文本的宽度信息,先来看下setTextAlign 的作用:

textPaint.setTextAlign(Paint.Align.CENTER) 是 Paint 类中的一个设置文本对齐方式的方法。它用于控制文本在指定位置绘制时的对齐方式,具体来说,它影响了绘制文本时文本的起始点(x,y 坐标)如何被确定。

作用:

Paint.Align 是一个枚举,定义了文本绘制时如何对齐相对于给定的 x 和 y 坐标。常用的对齐方式有三个选项:

  1. Paint.Align.LEFT:

    • 文本绘制时,文本的起始位置在 x 坐标上。
    • 也就是说,文本的左边缘会对齐到 x 坐标。
  2. Paint.Align.CENTER:

    • 文本绘制时,文本的中心位置与 x 坐标对齐。
    • 这意味着文本的中点(水平中心)会与 x 坐标对齐,从而实现水平居中。
  3. Paint.Align.RIGHT:

    • 文本绘制时,文本的右边缘会对齐到 x 坐标。
    • 也就是说,文本的右边缘会对齐到指定的 x 坐标,x 坐标即为文本的终点。

由于只能做到水平居中,想做到垂直居中还是要计算基线位置的!基线的两种计算方式可以参考前几篇文章,里面有详细的介绍

4. 定义自定义属性


  <declare-styleable name="CircularProgressBar">
        <!-- 进度条的最大值 -->
        <attr name="maxProgress" format="integer"/>
        <!-- 当前进度 -->
        <attr name="progress" format="integer"/>
        <!-- 环形进度条的背景色 -->
        <attr name="circleBackgroundColor" format="color"/>
        <!-- 进度条的颜色 -->
        <attr name="progressColor" format="color"/>
        <!-- 进度条的宽度 -->
        <attr name="circleWidth" format="dimension"/>
        <!-- 显示进度文本 -->
        <attr name="showProgressText" format="boolean"/>
        <!-- 进度文本的颜色 -->
        <attr name="progressTextColor" format="color"/>
        <!-- 进度文本的大小 -->
        <attr name="progressTextSize" format="dimension"/>
        <!-- 开始角度 -->
        <attr name="startAngle" format="enum">
            <enum name="angle0" value="0"/>
            <enum name="angle90" value="90"/>
            <enum name="angle180" value="180"/>
            <enum name="angle270" value="270"/>
        </attr>
    </declare-styleable>

5. 完整代码


public class CircularProgressBar extends View {

    private Paint backgroundPaint;
    private Paint progressPaint;
    private Paint textPaint;
    private RectF rectF;

    private int maxProgress = 100;
    private int progress = 0;
    private int circleBackgroundColor = Color.GRAY;
    private int progressColor = Color.GREEN;
    private int circleWidth = 20;
    private boolean showProgressText = true;
    private int progressTextColor = Color.BLACK;
    private int progressTextSize = 50;
    private int startAngle = 0; // 起始角度,默认从0开始

    public CircularProgressBar(Context context) {
        this(context, null);
    }

    public CircularProgressBar(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CircularProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        // 初始化自定义属性
        if (attrs != null) {
            TypedArray typedArray = context.getTheme().obtainStyledAttributes(
                    attrs,
                    R.styleable.CircularProgressBar,
                    0, 0
            );

            try {
                maxProgress = typedArray.getInt(R.styleable.CircularProgressBar_maxProgress, 100);
                progress = typedArray.getInt(R.styleable.CircularProgressBar_progress, 0);
                circleBackgroundColor = typedArray.getColor(R.styleable.CircularProgressBar_circleBackgroundColor, Color.GRAY);
                progressColor = typedArray.getColor(R.styleable.CircularProgressBar_progressColor, Color.GREEN);
                circleWidth = typedArray.getDimensionPixelSize(R.styleable.CircularProgressBar_circleWidth, 20);
                showProgressText = typedArray.getBoolean(R.styleable.CircularProgressBar_showProgressText, true);
                progressTextColor = typedArray.getColor(R.styleable.CircularProgressBar_progressTextColor, Color.BLACK);
                progressTextSize = typedArray.getDimensionPixelSize(R.styleable.CircularProgressBar_progressTextSize, 50);

                // 获取自定义的起始角度
                int angleValue = typedArray.getInt(R.styleable.CircularProgressBar_startAngle, 0);
                switch (angleValue) {
                    case 90:
                        startAngle = 90;
                        break;
                    case 180:
                        startAngle = 180;
                        break;
                    case 270:
                        startAngle = 270;
                        break;
                    default:
                        startAngle = 0;
                        break;
                }
            } finally {
                typedArray.recycle();
            }
        }

        // 设置画笔属性
        backgroundPaint = new Paint();
        backgroundPaint.setColor(circleBackgroundColor);
        backgroundPaint.setStyle(Paint.Style.STROKE);
        backgroundPaint.setStrokeWidth(circleWidth);
        backgroundPaint.setAntiAlias(true);

        progressPaint = new Paint();
        progressPaint.setColor(progressColor);
        progressPaint.setStyle(Paint.Style.STROKE);
        progressPaint.setStrokeWidth(circleWidth);
        progressPaint.setStrokeCap(Paint.Cap.ROUND);//圆角
        progressPaint.setAntiAlias(true);

        textPaint = new Paint();
        textPaint.setColor(progressTextColor);
        textPaint.setTextSize(progressTextSize);
        textPaint.setTextAlign(Paint.Align.CENTER);//文本对齐方式
        textPaint.setAntiAlias(true);

        rectF = new RectF();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        int padding = circleWidth / 2;
        rectF.set(padding, padding, w - padding, h - padding);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 绘制背景圆环
        canvas.drawArc(rectF, 0f, 360f, false, backgroundPaint);

        // 绘制进度圆环
        float sweepAngle = 360f * progress / (float) maxProgress;

        // 根据起始角度调整起始位置
        float adjustedStartAngle = 0f;
        switch (startAngle) {
            case 90:
                adjustedStartAngle = 0f;
                break;
            case 180:
                adjustedStartAngle = 90f;
                break;
            case 270:
                adjustedStartAngle = 180f;
                break;
            default:
                adjustedStartAngle = -90f; // 默认从0度(顶部)开始
                break;
        }

        canvas.drawArc(rectF, adjustedStartAngle, sweepAngle, false, progressPaint);

        // 绘制进度文本
        if (showProgressText) {
            String progressText = progress + "%";

            // 使用视图的中心来计算x和y坐标
            float x = getWidth() / 2f;
            float y = getHeight() / 2f - (textPaint.descent() + textPaint.ascent()) / 2f;

            // 绘制文本
            canvas.drawText(progressText, x, y, textPaint);
        }
    }

    // 设置进度
    public void setProgress(int progress) {
        if (progress > maxProgress) {
            this.progress = maxProgress;
        } else {
            this.progress = Math.max(progress, 0);
        }
        invalidate(); // 重新绘制
    }


    // 获取当前进度
    public int getProgress() {
        return progress;
    }
}

6. 使用示例


xml:

<com.xaye.example.CircularProgressBar
        android:id="@+id/circularProgressBar"
        android:layout_width="140dp"
        android:layout_height="140dp"
        app:maxProgress="100"
        app:circleBackgroundColor="#DDDDDD"
        app:progressColor="#00B8D4"
        app:circleWidth="15dp"
        app:showProgressText="true"
        app:progressTextColor="#000000"
        app:progressTextSize="30sp"
        app:startAngle="angle0"/>

Activity:

val animator = ValueAnimator.ofInt(0, 80)
            animator.setDuration(2000)
            animator.interpolator = LinearInterpolator()
            animator.addUpdateListener { animation ->
                val value = animation.animatedValue as Int
                mBind.circularProgressBar.setProgress(value)
            }
            animator.start()

7. 最后


本篇没什么难度,主要是介绍点新的东西,再熟悉熟悉手感,再会。

另外给喜欢记笔记的同学安利一款好用的云笔记软件,对比大部分国内的这个算还不错的,免费好用:wolai

Android 之 ProgressBar (进度条) 相信你看完2会吐槽,卧槽,这么坑爹,拿个动画来坑人,哈哈,实际开发中都这样,当然上述 这种情况只适用于不用显示进度的场合,如果要显示进度的场合就没用处了,好吧,接下来看下 网上一个简单的自定义圆形进度条!代码还是比较简单,容易理解,又兴趣可以看看,或者进行相关扩展~运行效果图:实现代码:/***///目标值,想改多少就改多少init();init();init();//完成相关参数初始化。 阅读详情

相关推荐

Android漂亮的横向和环形进度条示例

在这段XML代码中,我们指定了ProgressBar的样式为横向进度条,并将自定义样式custom_horizontal_progress应用到progressDrawable属性上。在这段XML代码中,我们指定了ProgressBar的样式为大型环形进度条,并将自定义样式custom_circular_progress应用到progressDrawable属性上。要创建一个漂亮的环形进度条,我们可以使用Android的ProgressBar组件,并为其应用自定义样式。这段代码将进度条的值设置为75。

ByteWhiz的博客 666

简单的自定义一个ProgressBar进度条

简单的自定义一个ProgressBar进度条

oAiYinSiTan12的博客 1214

自定义Android圆形进度条实现指南

本文还有配套的精品资源,点击获取 简介:在Android应用中,圆形进度条是提供给用户直观加载或进度反馈的实用控件。本教程将指导如何实现自定义圆形进度条,包括设置进度条样式、自定义属性(如颜色、大小)以及如何在代码中控制进度条的显示和动态更新。通过结合提供的图像资源和示例代码,你将掌握创建美观且实用的圆形进度条的完整流程。 1. Android圆形进度条基础 ...

weixin_35973118的博客 2693

Android 自定义View之圆形进度条

需求是不停的,会有这些需求:可指定画笔(宽度、颜色等)、可指定动画时长等。这些可以通过创建 Java 接口来设置,但我要学自定义View,就要用attr。未完待续~~Android Canvas的drawText()和文字居中方案。

weixin_44021334的博客 6223

Android环形进度条组件CircleProgress实战指南

本文还有配套的精品资源,点击获取 简介:在Android应用开发中,视觉效果和用户体验至关重要,而进度条是常见的UI元素之一。本文详细介绍如何在Android项目中集成和自定义环形进度条组件CircleProgress。CircleProgress是一个为Android设计的环形进度条库,提供美观、灵活的进度展示方式。本文指导如何将CircleProgress集成到你的An...

weixin_42561464的博客 2345

GAN模型中的常见变体有哪些,如DCGAN、WGAN和CGAN等?

生成对抗网络(GAN)是一种强大的深度学习模型,它由生成器和判别器两个网络组成,通过对抗训练的方式实现生成数据的能力。随着研究的发展,出现了许多GAN的变体,每个变体都有其独特的改进和特点。

huidhsu的博客 2260

重学 Android 自定义 View 系列(十):带指针的渐变环形进度条

前言该篇文章根据前面 重学 Android 自定义 View 系列(六):环形进度条 拓展而来。最终效果如下:1. 扩展功能支持进度顺时针或逆时针显示在进度条末尾添加自定义指针图片使用线性渐变为进度条添加颜色效果2. 关键技术点解析2.1 进度方向控制的实现通过添加一个 direction 属性,设置角度的正负,决定进度...

cnzzs的博客 1134

PADS安装教程(超详细)

声明:个人实践 一、工具准备 pads9.5完整破解版百度网盘提取码【wjx9】 链接:https://pan.baidu.com/s/1pIP6BJO435ssmF7Hjwyz8A 提取码:wjx9 安装环境:win10 64位 二、开始解压安装 关闭杀毒,exe以管理员身份运行 点击【skip】,不然又得licese 点击【agree】 点击【modify】 设置安装的工具、路径及工程路径 设置完成后,开始【install】 在之后注册。 将Crack复制到PADS安装目录

qq_42057393的博客 5万+

重学 Android 自定义 View 系列(十二):环形SeekBar剖析

一个自定义的圆形 SeekBar,类似于传统的 SeekBar 但采用了圆形轨迹。最近被一个网友私信问有没有类似效果的View,因为前面做过几个环形进度条,这个不就加个触摸效果么,以为不算很难,但深入了解后,才发现事情并没有那么简单…你需要具备的知识:三角函数正弦余弦计算、反三角函数、角度弧度区别…该View 由绘制圆形轨道(进度条):支持背景轨道和进度轨道两层绘制和背景圆环缓存。支持触摸交互:用户可以通过手指拖动控制进度。自定义进度范围:允许设置最小值、最大值以及当前进度。进度变化监听。

WriteBug001的博客 949

【小白指南】如何将deepseek引入WPS\Word\Excel中去?

如何将deepseek引入WPS\Word\Excel中去?纯小白都能看的懂,一步步操作就能行

2503_90498422的博客 1万+

OpenClaw-遇到的问题- FailoverError: Model context window too small (4096 tokens). Minimum is 16000

摘要:OpenClaw配置文件中deepseek-reasoner模型的上下文窗口被默认设置为4096 tokens(实际支持128K),导致系统报错"Model context window too small"。解决方案是删除配置文件中的contextWindow和maxTokens字段,让系统自动获取模型真实能力,并重启OpenClaw服务。配置文件通常位于用户目录下的.openclaw/openclaw.json中。

直到世界的尽头 4155

Android进度条ProgressBar使用详解

先介绍一下ProgressBar几种比较常用的属性 布局中设置: android:max="100" ——最大显示进度 android:progress="50"——第一显示进度 android:secondaryProgress="80"——第二显示进度 android:indeterminate="true"——设置是否精确显示,true表示不精确显示进度,false表示精确显示进度 ...

小旭的专栏 2万+

Mstar的Monitor方案OSD 菜单制作(五)——icon绘制

Mstar的Monitor方案(五)————绘制icon

android 系统定制开发那些事 1807

Android自定义圆环进度条/刻度仪表盘(单环单点带动画)

Android自定义圆环进度条/刻度仪表盘(单环单点带动画)

jin_pan的博客 968

C#实现自己的远程桌面控制工具

1. 创建一个winform工程并添加RDP客户端COM组件。右击工具箱,然后点击选择项,选中下图所示组件并点击确定。为窗口加载时添加自动连接远程桌面代码。3.调试运行程序,验证远程桌面控制。拖放RDP控件到窗口中。2.编写远程桌面连接代码。

6086

矿石生成(1)

这个提供了标准化的矿石放置配置模板简化了常见和稀有矿石的配置过程遵循了Minecraft原版的世界生成模式提高了代码的可读性和可维护性通过使用这个类,模组开发者可以更轻松地配置矿石的生成规则,而不需要每次都手动编写完整的放置修饰符列表。这种设计模式在原版Minecraft和其他大型模组中也很常见,是一种良好的编程实践。// 导入语句...// 字段和方法...包声明表明这个类位于luckymod模组的WorldGen包中,专门处理世界生成相关配置。导入语句。

qq_24992377的博客 136

Android - 进度条的使用

自定义一个类继承View绘制圆形进度条,然后引用,下面的代码可直接复制使用,代码中我暴露出了五个参数可以在使用时修改,java中调用先使用setmTargetProgress()方法修改进度值,然后调用invalidate()重新绘制view即可。//总进度值//以下五个参数已暴露出去,使用时可修改//进度值//圆形大小//绘制文本字体颜色//绘制进度颜色//绘制总进度颜色//绘制文本字体大小//进度粗细init();init();init();

2301_77563065的博客 4803

学Simulink——基于故障诊断与容错控制的场景实例:并网逆变器在短路故障下的动态响应及恢复策略

本文详细介绍了使用Simulink进行并网逆变器短路故障模拟与保护策略设计的方法。通过构建高精度逆变器模型,模拟IGBT击穿和桥臂直通故障,分析动态响应特性。重点设计了基于Stateflow的微秒级快速保护逻辑,包括过流检测、PWM闭锁和故障报警。同时提出完整的恢复策略流程,涵盖放电、去磁、自检和软启动等关键步骤。仿真结果表明,该方法能在200μs内有效限制短路电流,保护器件安全,并实现系统可靠恢复。文章还探讨了硬件保护协同、多级保护和预测保护等高级策略,为新能源系统的安全运行提供了重要参考。

amy_mhd的博客 143

Android 自定义View 之 圆环进度条

  很多时候我们会使用进度条,而Android默认的进度条是长条的,从左至右。而在日常开发中,有时候UI为了让页面更美观,就需要用到圆环进度条,那么本文就是通过自定义写一个圆环进度条,首先看一下效果图:  关于自定义View的基础知识就不再做过多的讲解了,我们直接进入正题,这一次我们不需要再去创建项目了,就用我之前创建的EasyView。  根据上面的效果图,我们首先来确定XML中的属性样式,修改attrs.xml的代码如下所示:   这里你会发现一个改变,那就是文字颜色和文字大小的属性从之前的中抽出来了,

晨曦 3897

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

圆环进度条 两种实现方式

圆环进度条 两种实现方式

唐僧的专栏 1万+

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

Android自定义ProgressBar

好久不写博客了,今天来总结一下自定义ProgressBar的实现。上周做一个游戏资源的在线更新功能,设计给的加载进度条设计图,是无法使用Android原生的ProgressBar来实现的。在百度和GitHub上搜了搜相关的资源,都不符合我的要求。于是,我只能自己去写。先给一下最终的效果: 上面就是我实现的ProgressBar的效果,与业务结合起来,分为几个阶...

zhaojun的博客 2万+

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Android 实现环形进度条

progressDrawable属性:确定进度条的背景,进度条颜色等等。项目中常常需要用到进度条,很简单,这儿做一个简单的总结和实现。mmmmmm.xml:drawable文件代码。style属性:确定他是什么类型的进度条。可以看出来这儿和上面是大不一样的。aaa drawable文件。里面的属性就是字面意思。

Android 领域开发程序员,希望通过写博客的方式不断的总结提升自己,大家共同进步哦! 3309

Android 基础知识4-3.7 ProgressBar(进度条)详解

ProgressBar(进度条)是UI界面中一种非常实用的组件,通常用于向用户像是某个耗时操作完成的百分比。进度条可动态地显示进度,因此避免长时间地执行某个操作时,让用户感觉程序失去了响应,从而更好地提高用户界面的友好性。也是为数不多的直接继承于View类的控件,直接子类有AbsSeekBar和ContentLoadingProgressBar,其中AbsSeekBar的子类有SeekBar和RatingBar。

yyxhzdm的博客 7390

自定义View之可拖拽的圆环进度条(android)

可手动控制的圆环进度条

一个黑曼巴的博客 2045

Android实现环形进度条(附带源码)

Android实现环形进度条(附带源码)

欢迎来到我的博客!这里是一个专注于计算机技术的分享平台,涵盖编程开发、算法研究、系统架构、软件工程等多个领域。无论你是初学者还是资深开发者,都能在这里找到有价值的内容。 1595
上一篇: 重学 Android 自定义 View 系列(五):文字变色的应用
下一篇: 重学 Android 自定义 View 系列(七):仿58同城加载动画
_蓝橙
博客等级 码龄7年 1179粉丝 174原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值