前言
在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>