前言
在进行Android开发时,会发现Android SDK中已经存在许多的UI组件如TextView、ImageView等,会受到SDK所提供功能的限制,这时候自定义视图就会变得很重要。
比如在进行已知进度等待时候,原生的进度条虽然可以显示进度,但无法显示数字,这就需要另加一个TextView了,所以今天决定自定义一个进度条,效果如下:
实现思路
初始化过程
首先,我们需要创建一个名为CircleProgressView的Kotlin类并继承自View,重写构造方法。
在Kotlin中使用@JvmOverloads代码变的非常简洁,比如下面这段,最终其实就生成了三个构造方法,和我们平时写的View初始化构造方法逻辑一样。
class CircleProgressView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
}
复制代码
接着还要对外开放一个方法,用来设置进度的值。
在这个过程中,我们要计算过度值,圆的一周是360,那么要显示50%的进度,就是画一半,那么绘制的值就是360f / 100f * 50
,从原来的值过度到目标值,选择ValueAnimator实现,加一个插值器,让他看起来不那么生硬。
var progress: Int = 0
set(value) {
field = value
ValueAnimator.ofFloat(sweepAngle, 360f / 100f * progress).apply {
interpolator = animationInterpolator
duration = 300
addUpdateListener { animation ->
sweepAngle = animation.animatedValue as Float
invalidate()
}
start()
}
}
复制代码
绘制过程
剩下的就是画圆了,可以通过drawArc方法,从效果图中能观察到,一共需要画两个圆,一个是底部,用来显示进度轮廓,一个是显示进度,轮廓就是单纯的一个圆圈,比如下面代码是用来绘制轮毂的。具体的进度值则根据变量进行绘制。
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
var rectF = RectF(32f, 32f, measuredWidth.toFloat()-32f, measuredHeight.toFloat()-32f)
var backgroundProgressPaint = Paint().apply {
isAntiAlias = true
color = Color.parseColor("#EFEFEF")
style = Paint.Style.STROKE
strokeCap = Paint.Cap.ROUND
strokeWidth = 32f
}
canvas.drawArc(rectF, 0f, 360f, false, backgroundProgressPaint)
}
复制代码
当然我们好要考虑View大小,既然是圆形,那么我们就要规定一个正方形的RectF,这个正方形的高宽是View的最小一边。
自定义属性
为了方便设置颜色样式,在加一个自定义属性。
<declare-styleable name="Progress">
<attr name="colorProgress" format="color"/>
<attr name="colorBackground" format="color"/>
<attr name="defaultValue" format="integer"/>
<attr name="strokeWidth" format="dimension"/>
</declare-styleable>
复制代码
全部代码如下
class CircleProgressView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
private val DP_IN_PX = Resources.getSystem().displayMetrics.density
private val DEFAULT_PROGRESS_STROKE_WIDTH = 16 * DP_IN_PX
private var strokeWithProgress = DEFAULT_PROGRESS_STROKE_WIDTH
private lateinit var progressBounds: RectF
private var sweepAngle: Float = 250f
private var centerX : Float = 0f
private var centerY : Float = 0f
private var defaultProgressColor = Color.parseColor("#55E552")
private var defaultBackGroundColor = Color.parseColor("#EFEFEF")
private val progressPaint: Paint
private val progressTextPaint: Paint
private val backgroundProgressPaint: Paint
private val animationInterpolator by lazy { DecelerateInterpolator() }
var progress: Int = 0
set(value) {
field = value
ValueAnimator.ofFloat(sweepAngle, 360f / 100f * progress).apply {
interpolator = animationInterpolator
duration = 300
addUpdateListener { animation ->
sweepAngle = animation.animatedValue as Float
invalidate()
Log.i("TAG", "value:$sweepAngle $progress "+(360f / 100f * progress))
}
start()
}
}
init {
val typedArray = context.obtainStyledAttributes(attrs,R.styleable.Progress)
val colorBackground= typedArray.getColor(R.styleable.Progress_colorBackground, defaultBackGroundColor)
val colorProgress= typedArray.getColor(R.styleable.Progress_colorProgress, defaultProgressColor)
val progressDefault = typedArray.getInteger(R.styleable.Progress_defaultValue, 0)
strokeWithProgress = typedArray.getDimension(R.styleable.Progress_strokeWidth, DEFAULT_PROGRESS_STROKE_WIDTH).toFloat()
progressPaint = Paint().apply {
isAntiAlias = true
color = colorProgress
style = Paint.Style.STROKE
strokeCap = Paint.Cap.ROUND
strokeWidth = strokeWithProgress
}
backgroundProgressPaint = Paint().apply {
isAntiAlias = true
color = colorBackground
style = Paint.Style.STROKE
strokeCap = Paint.Cap.ROUND
strokeWidth = strokeWithProgress
}
progressTextPaint = Paint().apply {
isAntiAlias = true
color = Color.BLACK
strokeWidth = 0f
textAlign = Paint.Align.CENTER
typeface = Typeface.create("cabin", Typeface.BOLD)
textSize = 120f
}
progress = progressDefault
}
override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) {
val viewWidth = layoutParams.width.toFloat()
val viewHeight = layoutParams.height.toFloat()
val arcStart = strokeWithProgress +50
val arcEnd = viewWidth - arcStart
progressBounds = RectF(arcStart, arcStart, arcEnd, arcEnd)
centerX = viewWidth/ 2f
centerY = viewHeight / 2f
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.drawArc(progressBounds, 90f, 360f, false, backgroundProgressPaint)
canvas.drawArc(progressBounds, 90f, sweepAngle, false, progressPaint)
canvas.drawText("$progress%", centerX, centerY + 35f, progressTextPaint)
}}
复制代码
使用方法
<com.example.kotlindemo.CircleProgressView
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_centerInParent="true"></com.example.kotlindemo.CircleProgressView>
复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END