随笔 - 129,  文章 - 3,  评论 - 50,  阅读 - 15万

kotlin版的自定义圆形进度条

大多数启动页都会带个进度条加载样式,所以就自己用kotlin重新写了一个,如果真的要很炫酷还是有很多东西可以附加的

一个简单的进度条基本组成就是一个背景环,一个进度环,需要注意的就是绘制的方式了

因为圆形进度条是宽高对等的,我们定义view宽高可能不是对等的,所以绘制的时候需要计算中间绘制,不然绘制的结果可能不是你理想的,比如下面右上角

 

 

 

当然,如果只是两个环,感觉有点丑,不是很美观,可以添加一点元素让它稍微的美观一点,比如绘制一个进度百分比提示文字,圆环自定义颜色,中心在绘制一个中心圆,这样通过配置颜色要比两个单调的圆环美观许多

然后还可以加入一点初始化或者结束的view动画,这边我加了一个进入缩放动画,开始拿起键盘一把梭

1.初始化

画笔,配置颜色背景,字体,进度等都需要用到自定义属性,所以初始化的时候需要把这些参数都实例化,然后刚才加入的缩放动画也是在初始化里

在xml里定义好style

复制代码
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="circleProgress">
        <attr name="circle_max" format="float" />
        <attr name="circle_progress" format="float" />
        <attr name="circle_width" format="dimension" />
        <attr name="circle_progress_color" format="color" />
        <attr name="circle_color" format="color" />
        <attr name="circle_text_color" format="color" />
        <attr name="circle_text_size" format="dimension" />
        <attr name="circle_text_isBold" format="boolean" />
        <attr name="circle_center_color" format="color" />
    </declare-styleable>
</resources>
复制代码

然后编写init方法

复制代码
private fun init() {
        //自定义属性
        val attributes: TypedArray = context.obtainStyledAttributes(
            attrs,
            R.styleable.circleProgress
        )
        mStrokeWidth = attributes.getDimension(R.styleable.circleProgress_circle_width, 1f)
        max = attributes.getFloat(R.styleable.circleProgress_circle_max, 100f)
        mProgress = attributes.getFloat(R.styleable.circleProgress_circle_progress, 0f)
        val bgColor = attributes.getColor(R.styleable.circleProgress_circle_color,Color.GRAY)
        val progressColor = attributes.getColor(R.styleable.circleProgress_circle_progress_color,Color.BLUE)
        val textColor = attributes.getColor(R.styleable.circleProgress_circle_text_color,Color.BLACK)
        val textSize = attributes.getDimension(R.styleable.circleProgress_circle_text_size,SizeUtils.dp2px(10f).toFloat())
        val isBold = attributes.getBoolean(R.styleable.circleProgress_circle_text_isBold,false)
        val centerColor = attributes.getColor(R.styleable.circleProgress_circle_center_color,Color.TRANSPARENT)
        attributes.recycle()
        //圆环画笔
        bgPaint = Paint()
        bgPaint.style = Paint.Style.STROKE
        bgPaint.strokeWidth = mStrokeWidth
        bgPaint.color = bgColor
        bgPaint.isAntiAlias = true
        //中心圆画笔
        centerPaint = Paint()
        centerPaint.style = Paint.Style.FILL
        centerPaint.color = centerColor
        centerPaint.isAntiAlias = true
        //进度条画笔
        tintPaint = Paint()
        tintPaint.style = Paint.Style.STROKE
        tintPaint.strokeWidth = mStrokeWidth
        tintPaint.strokeCap = Paint.Cap.ROUND
        tintPaint.color = progressColor
        tintPaint.isAntiAlias = true
        //文字画笔
        textPaint = Paint()
        textPaint.style = Paint.Style.FILL
        textPaint.textSize = textSize
        textPaint.textAlign = Paint.Align.CENTER
        textPaint.isFakeBoldText = isBold
        textPaint.color = textColor
        textPaint.isAntiAlias = true

        initAnimation()
    }
View Code
复制代码

2.测量

因为宽高是不可控的,你不知道具体设置成什么样子,有的是宽高尺寸一样,有的是宽大于高等,所以圆形直径需要取最小值,这样才能绘制成一个完整的圆形

复制代码
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        mWidth = getRealSize(widthMeasureSpec)
        mHeight = getRealSize(heightMeasureSpec)
        /** 直径 - 等宽高充满 | 当宽高不一致时,取最小的画圆 */
        val diameter = min(mWidth,mHeight)
        //半径
        mRadius = diameter / 2f - mStrokeWidth
        /** 进度条绘制区域 */
        val mX = mWidth/2f-diameter/2
        val mY = mHeight/2f-diameter/2
        mRect = RectF(mX + mStrokeWidth, mY + mStrokeWidth, mX + diameter - mStrokeWidth, mY + diameter - mStrokeWidth)

        setMeasuredDimension(mWidth,mHeight)
    }
复制代码

3.draw

圆形直接drawCircle绘制,进度条就需要绘制扇形了,然后通过传过来的进度条,计算扇形的角度,百分比文字就是在圆形中心点绘制

复制代码
override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        val progress = mProgress / max * 360
        //绘制圆形
        canvas!!.drawCircle(mWidth / 2f, mHeight / 2f, mRadius, bgPaint)
        //绘制中心圆
        canvas.drawCircle(mWidth / 2f, mHeight / 2f, mRadius, centerPaint)
        //绘制进度
        canvas.drawArc(mRect, -90f, progress, false, tintPaint)
        //绘制文字(百分比)
        val percentage: Int = (mProgress / max * 100).toInt()
        val centerY = mHeight / 2 + mStrokeWidth / 2
        canvas.drawText("${percentage}%", mWidth / 2f, centerY, textPaint)
    }
复制代码

就是这么简单,主要逻辑其实就在测量跟绘制

然后对外扩展设置进度,刷新视图

 

使用:

binding.pbTime.setProgress(count)

这里模拟一下加载进度,效果图见最上方

 

github:https://github.com/1024477951/KotlinStrong

posted on   翻滚的咸鱼  阅读(664)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
历史上的今天:
2015-03-11 观察者模式--初学入门
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示