当前位置: 首页 > news >正文

Android自定义一个带背景的圆环形进度条(Kotlin)

前言

在Android开发过程中,难免遇到一些复杂的UI组件需要我们自定义

当然使用系统原生组件拼凑也能完成,但是UI复杂度增加了不说,在更新UI状态的时候还不好管理,最重要的是复用的价值不大,上述的操作很容易引增加码冗余度和阅读难度,为此自定义UI成了一个非常不错的选择。

实现一个带进度条的播放按钮,类似于QQ音乐底部控住组件中的播放按钮。

1.ProgressBar组件

ProgressBar是Android开发中用于显示进度的一个UI组件。它通常用于向用户展示某个操作的进度情况,比如文件下载、数据加载等场景。ProgressBar可以以不同的形式展现,常见的有圆形进度条和水平进度条。

主要属性

  • max:进度条的最大值,默认为100。
  • progress:当前进度值。
  • progressDrawable:自定义进度条的Drawable。
  • indeterminate:是否设置为不确定模式,不确定模式的进度条会不断循环动画,而不会停止在一个特定的进度。
  • indeterminateDrawable:自定义不确定模式进度条的Drawable。
  • secondaryProgress:次要进度值,可以用于显示中间进度,比如缓冲进度。

使用场景

  • 下载文件时显示下载进度。
  • 加载数据时显示加载进度。
  • 视频播放时显示缓冲进度。
<ProgressBar  android:id="@+id/progressBar"  style="?android:attr/progressBarStyleHorizontal"  android:layout_width="match_parent"  android:layout_height="wrap_content"  android:max="100"  android:progress="50" />

2.自定义组件

继承View,在onDraw中使用Canvas绘制

背景

使用drawBitmap绘制背景

canvas.drawBitmap(it, null, rectF, null)

圆环

drawCircle绘制圆,其中Paint的style设置为STROKE可以绘制环形

strokeWidth就是圆环的半径

private val backgroundPaint = Paint()backgroundPaint.apply {color = backgroundColorstrokeWidth = 10fstyle = Paint.Style.STROKEisAntiAlias = false
}canvas.drawCircle(w, h, r, backgroundPaint)

进度条

drawArc实现环形进度条的绘制

rectF是整个环形的内切正方形坐标,这个正方形坐标需要和背景所在的圆环对上,否则可能出现进度条和背景对不上的问题

private val progressPaint = Paint()progressPaint.apply {color = progressColorstrokeWidth = 10fstyle = Paint.Style.STROKEisAntiAlias = false
}canvas.drawArc(rectF, 270f, progress.toFloat() / max.toFloat() * 360f, false, progressPaint)

 完整代码

class CircleBar : View {private val backgroundPaint = Paint()private val progressPaint = Paint()private var max = 100private var progress = 0private var backgroundColor: Int = Color.WHITEprivate var progressColor: Int = Color.BLACKprivate var backgroundBitmap: Bitmap? = nullprivate val rectF = RectF()private val offset = 5fconstructor(context: Context) : super(context)constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet) {init(context, attributeSet)}constructor(context: Context, attributeSet: AttributeSet, defStyleAttr: Int) : super(context,attributeSet,defStyleAttr) {init(context, attributeSet)}private fun init(context: Context, attributeSet: AttributeSet) {val typedArray = context.obtainStyledAttributes(attributeSet, R.styleable.CircleBar)max = typedArray.getInteger(R.styleable.CircleBar_max, 100)progress = typedArray.getInteger(R.styleable.CircleBar_progress, 0)backgroundColor = typedArray.getColor(R.styleable.CircleBar_backgroundColor, Color.WHITE)progressColor = typedArray.getColor(R.styleable.CircleBar_progressColor, Color.BLACK)val background = typedArray.getDrawable(R.styleable.CircleBar_background)if (background != null) {backgroundBitmap = (background as BitmapDrawable).bitmap}typedArray.recycle()backgroundPaint.apply {color = backgroundColorstrokeWidth = 10fstyle = Paint.Style.STROKEisAntiAlias = false}progressPaint.apply {color = progressColorstrokeWidth = 10fstyle = Paint.Style.STROKEisAntiAlias = false}}override fun onDraw(canvas: Canvas) {super.onDraw(canvas)val w = width / 2fval h = height / 2fval r = w - offsetrectF.set(offset, h - r, w + r, h + r)backgroundBitmap?.let { canvas.drawBitmap(it, null, rectF, null) }canvas.drawCircle(w, h, r, backgroundPaint)canvas.drawArc(rectF, 270f, progress.toFloat() / max.toFloat() * 360f, false, progressPaint)}fun setMax(max: Int) {this.max = maxinvalidate()}fun setProgress(progress: Int) {this.progress = progressinvalidate()}fun getMax(): Int {return max}}

组件主题样式

<declare-styleable name="CircleBar"><attr name="max" format="integer" /><attr name="progress" format="integer" /><attr name="backgroundColor" format="color" /><attr name="progressColor" format="color" /><attr name="background" format="reference" />
</declare-styleable>

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 如何解决大数据背景下的数据治理挑战
  • Oracle PL/SQL存储过程和函数简单示例
  • AI工具革新:国内外设计艺术的融合
  • WRF-LES与PALM微尺度气象大涡模拟
  • 鸿蒙内核源码分析(Fork篇) | 一次调用,两次返回
  • QT 文件
  • AI视频创作原理
  • 3-4 STM32F405--定时器输入捕获
  • 【机器学习】3. 欧式距离,曼哈顿距离,Minkowski距离,加权欧式距离
  • 【Python】FastAPI:路径操作
  • 【图像去雨】MPRNet:性能炸裂!MPRNet:多阶段渐进式图像恢复(图像去雨、去模糊、去噪)
  • 【面试题系列Vue04】Vue.js中 $nextTick 原理及作用
  • 《黑神话:悟空》的AI技术解析:游戏智能的新境界
  • WPS Office两个严重漏洞曝光,已被武器化且在野利用
  • Spring Boot中的过滤器与拦截器实战:实现用户认证与资源访问控制
  • $translatePartialLoader加载失败及解决方式
  • 【347天】每日项目总结系列085(2018.01.18)
  • 【vuex入门系列02】mutation接收单个参数和多个参数
  • Android 架构优化~MVP 架构改造
  • GraphQL学习过程应该是这样的
  • HTTP中GET与POST的区别 99%的错误认识
  • Laravel Telescope:优雅的应用调试工具
  • Laravel核心解读--Facades
  • Mac 鼠须管 Rime 输入法 安装五笔输入法 教程
  • mysql_config not found
  • open-falcon 开发笔记(一):从零开始搭建虚拟服务器和监测环境
  • rc-form之最单纯情况
  • spring学习第二天
  • 彻底搞懂浏览器Event-loop
  • 分享一个自己写的基于canvas的原生js图片爆炸插件
  • 面试题:给你个id,去拿到name,多叉树遍历
  • -- 数据结构 顺序表 --Java
  • 详解NodeJs流之一
  • 写代码的正确姿势
  • 正则表达式小结
  • 整理一些计算机基础知识!
  • 支付宝花15年解决的这个问题,顶得上做出十个支付宝 ...
  • ​DB-Engines 11月数据库排名:PostgreSQL坐稳同期涨幅榜冠军宝座
  • ​MPV,汽车产品里一个特殊品类的进化过程
  • ​浅谈 Linux 中的 core dump 分析方法
  • ​一文看懂数据清洗:缺失值、异常值和重复值的处理
  • $redis-setphp_redis Set命令,php操作Redis Set函数介绍
  • (13)[Xamarin.Android] 不同分辨率下的图片使用概论
  • (2)关于RabbitMq 的 Topic Exchange 主题交换机
  • (BAT向)Java岗常问高频面试汇总:MyBatis 微服务 Spring 分布式 MySQL等(1)
  • (DenseNet)Densely Connected Convolutional Networks--Gao Huang
  • (Matalb时序预测)WOA-BP鲸鱼算法优化BP神经网络的多维时序回归预测
  • (Spark3.2.0)Spark SQL 初探: 使用大数据分析2000万KF数据
  • (十六)一篇文章学会Java的常用API
  • (十三)Maven插件解析运行机制
  • (十五)Flask覆写wsgi_app函数实现自定义中间件
  • (使用vite搭建vue3项目(vite + vue3 + vue router + pinia + element plus))
  • (一)Kafka 安全之使用 SASL 进行身份验证 —— JAAS 配置、SASL 配置
  • (原)Matlab的svmtrain和svmclassify
  • (转)Linq学习笔记