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 = backgroundColor
    strokeWidth = 10f
    style = Paint.Style.STROKE
    isAntiAlias = false
}


canvas.drawCircle(w, h, r, backgroundPaint)

进度条

drawArc实现环形进度条的绘制

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

private val progressPaint = Paint()

progressPaint.apply {
    color = progressColor
    strokeWidth = 10f
    style = Paint.Style.STROKE
    isAntiAlias = 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 = 100
    private var progress = 0
    private var backgroundColor: Int = Color.WHITE
    private var progressColor: Int = Color.BLACK
    private var backgroundBitmap: Bitmap? = null
    private val rectF = RectF()
    private val offset = 5f

    constructor(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 = backgroundColor
            strokeWidth = 10f
            style = Paint.Style.STROKE
            isAntiAlias = false
        }
        progressPaint.apply {
            color = progressColor
            strokeWidth = 10f
            style = Paint.Style.STROKE
            isAntiAlias = false
        }
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        val w = width / 2f
        val h = height / 2f
        val r = w - offset
        rectF.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 = max
        invalidate()
    }

    fun setProgress(progress: Int) {
        this.progress = progress
        invalidate()
    }

    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>

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会写代码的猴子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值