Custom arc progress bar (Kotlin&Compose)

After starting to use compose, I found out that some views that needed to be customized before, with dozens or hundreds of lines of code, need to customize layout attributes, need to inherit View, and need to use Paint. If you use Compose, you can directly use 20 lines of one method solve.

To implement a custom arc progress bar as shown in the figure below:

arc progress bar

Implemented using Kotlin

In the early days, I wrote such a small demo
complete code: https://github.com/yaoxiawen/CircleProgressBar

custom layout properties

Define the required attributes in attrs.xml (if not created)

    <!--圆环形进度条-->
    <declare-styleable name="CircleProgressBar">
        <attr name="ringMax" format="integer" />
        <attr name="progress" format="integer" />
        <attr name="startAngle" format="float" />
        <attr name="endAngle" format="float" />
        <attr name="reverse" format="boolean" />
        <attr name="roundCap" format="boolean" />
        <attr name="ringWidth" format="dimension" />
        <attr name="ringColor" format="color" />
        <attr name="ringBackgroundColor" format="color" />
    </declare-styleable>

Custom View

Create CircleProgressBar to inherit View

class CircleProgressBar @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) 

Parse attributes through AttributeSet

Parse the attribute values ​​passed in the layout through the AttributeSet parameter in the constructor, and all attributes need to have default values

        val ta = context.obtainStyledAttributes(attrs, R.styleable.CircleProgressBar)
        max = ta.getInt(R.styleable.CircleProgressBar_ringMax, DEFAULT_MAX)
        if (max <= 0) {
            max = DEFAULT_MAX
        }
        startAngle = ta.getFloat(R.styleable.CircleProgressBar_startAngle, DEFAULT_START_ANGLE)
        endAngle = ta.getFloat(R.styleable.CircleProgressBar_endAngle, DEFAULT_END_ANGLE)
        reverse = ta.getBoolean(R.styleable.CircleProgressBar_reverse, DEFAULT_REVERSE)
        roundCap = ta.getBoolean(R.styleable.CircleProgressBar_roundCap, DEFAULT_ROUND_CAP)
        progress = ta.getInt(R.styleable.CircleProgressBar_progress, DEFAULT_PROGRESS)
        ringWidth =
            ta.getDimension(R.styleable.CircleProgressBar_ringWidth, DEFAULT_RING_WIDTH)
        ringColor = ta.getColor(R.styleable.CircleProgressBar_ringColor, DEFAULT_RING_COLOR)
        ringBackgroungColor = ta.getColor(
            R.styleable.CircleProgressBar_ringBackgroundColor,
            DEFAULT_RING_BACKGROUND_COLOR
        )
        ta.recycle()

Initialize Paint

Custom view uses canvas to draw, naturally need to have Paint

        mArcPaint = Paint()
        mArcPaint.isAntiAlias = true

Draw an arc in onDraw

Use drawArc of canvas to draw an arc, and set strokeCap ​​to Paint.Cap.ROUND in Paint to draw the effect of a round cap.

        with(mArcPaint) {
            color = ringBackgroungColor
            style = Paint.Style.STROKE
            strokeWidth = ringWidth
            if (roundCap) {
                strokeCap = Paint.Cap.ROUND
            }
        }
        //绘制圆环背景
        canvas.drawArc(mRectF, startAngle, endAngle - startAngle, false, mArcPaint)
        with(mArcPaint) {
            color = ringColor
            if (roundCap) {
                strokeCap = Paint.Cap.ROUND
            }
        }
        var sweepAngle = progress.toFloat() / max * (endAngle - startAngle)
        if (reverse) {
            sweepAngle = -sweepAngle  //逆时针滚动
        }
        canvas.drawArc(mRectF, startAngle, sweepAngle, false, mArcPaint)

xml layout used in

    <com.example.circleprogressbar.CircleProgressBar
        android:layout_width="200dp"
        android:layout_height="200dp"
        app:progress="30"
        app:startAngle="-225"
        app:endAngle="45"
        app:ringWidth="10dp" />

Implemented using Compose

For some similar APIs, use drawArc of Canvas to draw arcs, and set cap = StrokeCap.Round to achieve the effect of a round cap.

@Composable
fun CircleProgress(
    modifier: Modifier = Modifier,
    progress: Int,
    startAngle: Float,
    endAngle: Float,
    progressBgColor: Color,
    progressColor: Color,
) {
    Canvas(
        modifier = modifier,
        onDraw = {
            drawArc(
                color = progressBgColor,
                startAngle = startAngle,
                sweepAngle = endAngle - startAngle,
                useCenter = false,
                style = Stroke(size.width / 11, cap = StrokeCap.Round)
            )
            drawArc(
                color = progressColor,
                startAngle = startAngle,
                sweepAngle = (endAngle - startAngle) * progress / 100,
                useCenter = false,
                style = Stroke(size.width / 11, cap = StrokeCap.Round)
            )
        }
    )
}

Call the method to pass in some corresponding parameters, and the parameters can also be directly set with default values.

        CircleProgress(
            modifier = Modifier.padding(5.dp).size(66.dp),
            startAngle = -225f,
            endAngle = 45f,
            progress = 30,
            progressBgColor = Color.LightGray,
            progressColor = Color.Blue
        )

Guess you like

Origin blog.csdn.net/yuantian_shenhai/article/details/131603362