抛弃原生进度条,百行内从零开始自定义

前言

在进行Android开发时,会发现Android SDK中已经存在许多的UI组件如TextView、ImageView等,会受到SDK所提供功能的限制,这时候自定义视图就会变得很重要。

比如在进行已知进度等待时候,原生的进度条虽然可以显示进度,但无法显示数字,这就需要另加一个TextView了,所以今天决定自定义一个进度条,效果如下:

录屏_选择区域_20210512093235.gif

实现思路

初始化过程

首先,我们需要创建一个名为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)
}
复制代码

image.png

当然我们好要考虑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
喜欢就支持一下吧
点赞0 分享