Android 自定义圆弧进度条

本贴最后更新于 2796 天前,其中的信息可能已经时过境迁

挺久没写文章了,近段时间被拉过去写 JS 项目了,在做一个项目的时候,遇到一个新的需求就是空气质量,实现空气污染指数的时候,需要到一个圆弧的进度,在网上没找到合适,干脆就自己写了一个,顺便复习一下自定义 View,下面是具体的实现。

先看一下效果

这里的话我只做一个进度条,使用也很简单。圆弧外的文本是一个 textview,不是这个控件里面的,说明一下。

下面先看一下整体的代码:

import android.animation.ValueAnimator; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.view.View; public class ArcProgressBar extends View { private static final String TAG = "ArcProgressBar"; /** * 圆弧的宽度 */ private int mStrokeWidth = dp2px(8); /** * 圆弧开始的角度 */ private float mStartAngle = 135; /** * 起点角度和终点角度对应的夹角大小 */ private float mAngleSize = 270; /** * 圆弧背景颜色 */ private int mArcBgColor = Color.YELLOW; /** * 最大的进度,用于计算进度与夹角的比例 */ private float mMaxProgress = 500; /** * 当前进度对应的起点角度到当前进度角度夹角的大小 */ private float mCurrentAngleSize = 0; /** * 当前进度 */ private float mCurrentProgress = 0; /** * 动画的执行时长 */ private long mDuration = 3000; /** * 进度圆弧的颜色 */ private int mProgressColor = Color.RED; /** * 第一行文本 */ private String mFirstText = "42"; /** * 第一行文本的颜色 */ private int mFirstTextColor = Color.RED; /** * 第一行文本的字体大小 */ private float mFirstTextSize = 56f; /** * 第二行文本 */ private String mSecondText = "优"; /** * 第二行文本的颜色 */ private int mSecondTextColor = Color.RED; /** * 第二行文本的字体大小 */ private float mSecondTextSize = 56f; public ArcProgressBar(Context context) { super(context, null); } public ArcProgressBar(Context context, @Nullable AttributeSet attrs) { super(context, attrs, 0); } public ArcProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initAttr(context, attrs); } /** * 设置初始化的参数 * * @param context * @param attrs */ private void initAttr(Context context, AttributeSet attrs) { TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ArcProgressBar); mMaxProgress = array.getFloat(R.styleable.ArcProgressBar_arc_max_progress, 500f); mArcBgColor = array.getColor(R.styleable.ArcProgressBar_arc_bg_color, Color.YELLOW); mStrokeWidth = dp2px(array.getDimension(R.styleable.ArcProgressBar_arc_stroke_width, 12f)); mCurrentProgress = array.getFloat(R.styleable.ArcProgressBar_arc_progress, 300f); mProgressColor = array.getColor(R.styleable.ArcProgressBar_arc_progress_color, Color.RED); mFirstText = array.getString(R.styleable.ArcProgressBar_arc_first_text); mFirstTextSize = dp2px(array.getDimension(R.styleable.ArcProgressBar_arc_first_text_size, 20f)); mFirstTextColor = array.getColor(R.styleable.ArcProgressBar_arc_first_text_color, Color.RED); mSecondText = array.getString(R.styleable.ArcProgressBar_arc_second_text); mSecondTextSize = dp2px(array.getDimension(R.styleable.ArcProgressBar_arc_second_text_size, 20f)); mSecondTextColor = array.getColor(R.styleable.ArcProgressBar_arc_second_text_color, Color.RED); mAngleSize = array.getFloat(R.styleable.ArcProgressBar_arc_angle_size, 270f); mStartAngle = array.getFloat(R.styleable.ArcProgressBar_arc_start_angle, 135f); array.recycle(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int centerX = getWidth() / 2; RectF rectF = new RectF(); rectF.left = mStrokeWidth; rectF.top = mStrokeWidth; rectF.right = centerX * 2 - mStrokeWidth; rectF.bottom = centerX * 2 - mStrokeWidth; //画最外层的圆弧 drawArcBg(canvas, rectF); //画进度 drawArcProgress(canvas, rectF); //绘制第一级文本 drawFirstText(canvas, centerX); //绘制第二级文本 drawSecondText(canvas, centerX); } /** * 画最开始的圆弧 * * @param canvas * @param rectF */ private void drawArcBg(Canvas canvas, RectF rectF) { Paint mPaint = new Paint(); //画笔的填充样式,Paint.Style.FILL 填充内部;Paint.Style.FILL_AND_STROKE 填充内部和描边;Paint.Style.STROKE 描边 mPaint.setStyle(Paint.Style.STROKE); //圆弧的宽度 mPaint.setStrokeWidth(mStrokeWidth); //抗锯齿 mPaint.setAntiAlias(true); //画笔的颜色 mPaint.setColor(mArcBgColor); //画笔的样式 Paint.Cap.Round 圆形,Cap.SQUARE 方形 mPaint.setStrokeCap(Paint.Cap.ROUND); //开始画圆弧 canvas.drawArc(rectF, mStartAngle, mAngleSize, false, mPaint); } /** * 画进度的圆弧 * * @param canvas * @param rectF */ private void drawArcProgress(Canvas canvas, RectF rectF) { Paint paint = new Paint(); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(mStrokeWidth); paint.setColor(mProgressColor); paint.setAntiAlias(true); paint.setStrokeCap(Paint.Cap.ROUND); canvas.drawArc(rectF, mStartAngle, mCurrentAngleSize, false, paint); } /** * 绘制第一级文字 * * @param canvas 画笔 * @param centerX 位置 */ private void drawFirstText(Canvas canvas, float centerX) { Paint paint = new Paint(); paint.setAntiAlias(true); paint.setColor(mFirstTextColor); paint.setTextAlign(Paint.Align.CENTER); paint.setTextSize(mFirstTextSize); Rect firstTextBounds = new Rect(); paint.getTextBounds(mFirstText, 0, mFirstText.length(), firstTextBounds); canvas.drawText(mFirstText, centerX, firstTextBounds.height() / 2 + getHeight() * 2 / 5, paint); } /** * 绘制第二级文本 * * @param canvas 画笔 * @param centerX 文本 */ private void drawSecondText(Canvas canvas, float centerX) { Paint paint = new Paint(); paint.setAntiAlias(true); paint.setColor(mSecondTextColor); paint.setTextAlign(Paint.Align.CENTER); paint.setTextSize(mSecondTextSize); Rect bounds = new Rect(); paint.getTextBounds(mSecondText, 0, mSecondText.length(), bounds); canvas.drawText(mSecondText, centerX, getHeight() / 2 + bounds.height() / 2 + getFontHeight(mSecondText, mSecondTextSize), paint); } /** * 设置最大的进度 * * @param progress */ public void setMaxProgress(int progress) { if (progress < 0) { throw new IllegalArgumentException("Progress value can not be less than 0 "); } mMaxProgress = progress; } /** * 设置当前进度 * * @param progress */ public void setProgress(float progress) { if (progress < 0) { throw new IllegalArgumentException("Progress value can not be less than 0"); } if (progress > mMaxProgress) { progress = mMaxProgress; } mCurrentProgress = progress; float size = mCurrentProgress / mMaxProgress; mCurrentAngleSize = (int) (mAngleSize * size); setAnimator(0, mCurrentAngleSize); } /** * 设置进度圆弧的颜色 * * @param color */ public void setProgressColor(int color) { if (color == 0) { throw new IllegalArgumentException("Color can no be 0"); } mProgressColor = color; } /** * 设置圆弧的颜色 * * @param color */ public void setArcBgColor(int color) { if (color == 0) { throw new IllegalArgumentException("Color can no be 0"); } mArcBgColor = color; } /** * 设置圆弧的宽度 * * @param strokeWidth */ public void setStrokeWidth(int strokeWidth) { if (strokeWidth < 0) { throw new IllegalArgumentException("strokeWidth value can not be less than 0"); } mStrokeWidth = dp2px(strokeWidth); } /** * 设置动画的执行时长 * * @param duration */ public void setAnimatorDuration(long duration) { if (duration < 0) { throw new IllegalArgumentException("Duration value can not be less than 0"); } mDuration = duration; } /** * 设置第一行文本 * * @param text */ public void setFirstText(String text) { mFirstText = text; } /** * 设置第一行文本的颜色 * * @param color */ public void setFirstTextColor(int color) { if (color <= 0) { throw new IllegalArgumentException("Color value can not be less than 0"); } mFirstTextColor = color; } /** * 设置第一行文本的大小 * * @param textSize */ public void setFirstTextSize(float textSize) { if (textSize <= 0) { throw new IllegalArgumentException("textSize can not be less than 0"); } mFirstTextSize = textSize; } /** * 设置第二行文本 * * @param text */ public void setSecondText(String text) { mSecondText = text; } /** * 设置第二行文本的颜色 * * @param color */ public void setSecondTextColor(int color) { if (color == 0) { throw new IllegalArgumentException("Color value can not be less than 0"); } mSecondTextColor = color; } /** * 设置第二行文本的大小 * * @param textSize */ public void setSecondTextSize(float textSize) { if (textSize <= 0) { throw new IllegalArgumentException("textSize can not be less than 0"); } mSecondTextSize = textSize; } /** * 设置圆弧开始的角度 * * @param startAngle */ public void setStartAngle(int startAngle) { mStartAngle = startAngle; } /** * 设置圆弧的起始角度到终点角度的大小 * * @param angleSize */ public void setAngleSize(int angleSize) { mAngleSize = angleSize; } /** * dp转成px * * @param dp * @return */ private int dp2px(float dp) { float density = getResources().getDisplayMetrics().density; return (int) (dp * density + 0.5f * (dp >= 0 ? 1 : -1)); } /** * 设置动画 * * @param start 开始位置 * @param target 结束位置 */ private void setAnimator(float start, float target) { ValueAnimator valueAnimator = ValueAnimator.ofFloat(start, target); valueAnimator.setDuration(mDuration); valueAnimator.setTarget(mCurrentAngleSize); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { mCurrentAngleSize = (float) valueAnimator.getAnimatedValue(); invalidate(); } }); valueAnimator.start(); } /** * 测量字体的高度 * * @param textStr * @param fontSize * @return */ private float getFontHeight(String textStr, float fontSize) { Paint paint = new Paint(); paint.setTextSize(fontSize); Rect bounds = new Rect(); paint.getTextBounds(textStr, 0, textStr.length(), bounds); return bounds.height(); } }

然后是具体使用控件里面的代码:

android:id="@+id/arc" android:layout_width="100dp" android:layout_height="100dp" android:layout_gravity="center"/>

这里使用非常简单,跟其它控件的使用没什么区别。

里面还定义了很多直接在 xml 文件里面就可以直接设置的属性

这个自定义 View 非常简单,代码里面注释也写的很清楚了,就不做过多的介绍了,有兴趣的可以去做更多的扩展,比如,直接做成圆形或者扇形,还有这里必须要两行文本,如果项目需要,也可以直接去删除掉文本,不过需要计算一下文本的绘制位置。

有兴趣的可以去 GitHub 上面给个 Start,地址是:传送门

  • Android

    Android 是一种以 Linux 为基础的开放源码操作系统,主要使用于便携设备。2005 年由 Google 收购注资,并拉拢多家制造商组成开放手机联盟开发改良,逐渐扩展到到平板电脑及其他领域上。

    335 引用 • 324 回帖
  • 进度条
    1 引用 • 1 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • Flume

    Flume 是一套分布式的、可靠的,可用于有效地收集、聚合和搬运大量日志数据的服务架构。

    9 引用 • 6 回帖 • 652 关注
  • 一些有用的避坑指南。

    69 引用 • 93 回帖
  • CAP

    CAP 指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可兼得。

    12 引用 • 5 回帖 • 640 关注
  • 强迫症

    强迫症(OCD)属于焦虑障碍的一种类型,是一组以强迫思维和强迫行为为主要临床表现的神经精神疾病,其特点为有意识的强迫和反强迫并存,一些毫无意义、甚至违背自己意愿的想法或冲动反反复复侵入患者的日常生活。

    15 引用 • 161 回帖 • 3 关注
  • OpenStack

    OpenStack 是一个云操作系统,通过数据中心可控制大型的计算、存储、网络等资源池。所有的管理通过前端界面管理员就可以完成,同样也可以通过 Web 接口让最终用户部署资源。

    10 引用 • 2 关注
  • NGINX

    NGINX 是一个高性能的 HTTP 和反向代理服务器,也是一个 IMAP/POP3/SMTP 代理服务器。 NGINX 是由 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的,第一个公开版本 0.1.0 发布于 2004 年 10 月 4 日。

    315 引用 • 547 回帖
  • 钉钉

    钉钉,专为中国企业打造的免费沟通协同多端平台, 阿里巴巴出品。

    15 引用 • 67 回帖 • 297 关注
  • Latke

    Latke 是一款以 JSON 为主的 Java Web 框架。

    71 引用 • 535 回帖 • 824 关注
  • 服务

    提供一个服务绝不仅仅是简单的把硬件和软件累加在一起,它包括了服务的可靠性、服务的标准化、以及对服务的监控、维护、技术支持等。

    41 引用 • 24 回帖 • 3 关注
  • Eclipse

    Eclipse 是一个开放源代码的、基于 Java 的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。

    76 引用 • 258 回帖 • 631 关注
  • Follow
    4 引用 • 12 回帖 • 11 关注
  • GAE

    Google App Engine(GAE)是 Google 管理的数据中心中用于 WEB 应用程序的开发和托管的平台。2008 年 4 月 发布第一个测试版本。目前支持 Python、Java 和 Go 开发部署。全球已有数十万的开发者在其上开发了众多的应用。

    14 引用 • 42 回帖 • 809 关注
  • AngularJS

    AngularJS 诞生于 2009 年,由 Misko Hevery 等人创建,后为 Google 所收购。是一款优秀的前端 JS 框架,已经被用于 Google 的多款产品当中。AngularJS 有着诸多特性,最为核心的是:MVC、模块化、自动化双向数据绑定、语义化标签、依赖注入等。2.0 版本后已经改名为 Angular。

    12 引用 • 50 回帖 • 504 关注
  • 域名

    域名(Domain Name),简称域名、网域,是由一串用点分隔的名字组成的 Internet 上某一台计算机或计算机组的名称,用于在数据传输时标识计算机的电子方位(有时也指地理位置)。

    43 引用 • 208 回帖
  • Kafka

    Kafka 是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者规模的网站中的所有动作流数据。 这种动作(网页浏览,搜索和其他用户的行动)是现代系统中许多功能的基础。 这些数据通常是由于吞吐量的要求而通过处理日志和日志聚合来解决。

    36 引用 • 35 回帖 • 2 关注
  • Git

    Git 是 Linux Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。

    211 引用 • 358 回帖
  • PHP

    PHP(Hypertext Preprocessor)是一种开源脚本语言。语法吸收了 C 语言、 Java 和 Perl 的特点,主要适用于 Web 开发领域,据说是世界上最好的编程语言。

    179 引用 • 408 回帖 • 486 关注
  • 持续集成

    持续集成(Continuous Integration)是一种软件开发实践,即团队开发成员经常集成他们的工作,通过每个成员每天至少集成一次,也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,从而尽早地发现集成错误。

    15 引用 • 7 回帖
  • Mac

    Mac 是苹果公司自 1984 年起以“Macintosh”开始开发的个人消费型计算机,如:iMac、Mac mini、Macbook Air、Macbook Pro、Macbook、Mac Pro 等计算机。

    169 引用 • 595 回帖
  • OkHttp

    OkHttp 是一款 HTTP & HTTP/2 客户端库,专为 Android 和 Java 应用打造。

    16 引用 • 6 回帖 • 83 关注
  • WiFiDog

    WiFiDog 是一套开源的无线热点认证管理工具,主要功能包括:位置相关的内容递送;用户认证和授权;集中式网络监控。

    1 引用 • 7 回帖 • 611 关注
  • SOHO

    为成为自由职业者在家办公而努力吧!

    7 引用 • 55 回帖 • 4 关注
  • Wide

    Wide 是一款基于 Web 的 Go 语言 IDE。通过浏览器就可以进行 Go 开发,并有代码自动完成、查看表达式、编译反馈、Lint、实时结果输出等功能。

    欢迎访问我们运维的实例: https://wide.b3log.org

    30 引用 • 218 回帖 • 643 关注
  • 外包

    有空闲时间是接外包好呢还是学习好呢?

    26 引用 • 233 回帖 • 3 关注
  • MongoDB

    MongoDB(来自于英文单词“Humongous”,中文含义为“庞大”)是一个基于分布式文件存储的数据库,由 C++ 语言编写。旨在为应用提供可扩展的高性能数据存储解决方案。MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,是类似 JSON 的 BSON 格式,因此可以存储比较复杂的数据类型。

    90 引用 • 59 回帖 • 7 关注
  • Ubuntu

    Ubuntu(友帮拓、优般图、乌班图)是一个以桌面应用为主的 Linux 操作系统,其名称来自非洲南部祖鲁语或豪萨语的“ubuntu”一词,意思是“人性”、“我的存在是因为大家的存在”,是非洲传统的一种价值观,类似华人社会的“仁爱”思想。Ubuntu 的目标在于为一般用户提供一个最新的、同时又相当稳定的主要由自由软件构建而成的操作系统。

    127 引用 • 169 回帖
  • Maven

    Maven 是基于项目对象模型(POM)、通过一小段描述信息来管理项目的构建、报告和文档的软件项目管理工具。

    186 引用 • 318 回帖 • 255 关注