欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

自定义圆环进度条

程序员文章站 2024-02-06 23:47:34
...

 

自定义圆环进度条

自定义圆环进度条


import android.animation.ValueAnimator
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import android.view.animation.LinearInterpolator
import com.android.smartbath.R

/**
 * <pre>
 * Renovation by DengDongQi on 2020/5/12
 * 自定义圆环
 * </pre>
 */
class CalibrationCircleView : View {
    //初始化标记
    private var init = false
    //是否使用渐变
    var useGradient = false
        set(value) {
            field = value
            init()
        }

    // 默认的起始渐变色
    var defStartColor = Color.RED
    // 默认的中间渐变色
    var defCenterColor = Color.YELLOW
    // 默认的结束渐变色
    var defendColor = Color.BLUE

    //环形颜色
    var ringColor = Color.GREEN
        set(value) {
            field = value
            init()
        }
    //环形背景色
    var ringBgColor = Color.TRANSPARENT
        set(value) {
            field = value
            init()
        }
    //起始颜色
    var startColor = Color.TRANSPARENT
        set(value) {
            field = value
            if (init && value!=Color.TRANSPARENT) {
                useGradient = true
            }
        }
    //中间颜色
    private var centerColor = Color.TRANSPARENT
        set(value) {
            field = value
            if (init && value!=Color.TRANSPARENT) {
                useGradient = true
            }
        }
    //结束颜色
    private var endColor = Color.TRANSPARENT
        set(value) {
            field = value
            if (init && value!=Color.TRANSPARENT) {
                useGradient = true
            }
        }
    //线宽
    var roundWidth = 8
        set(value) {
            field = value
            init()
        }

    //线帽样式
    var strokeCap = Paint.Cap.BUTT
        set(value) {
            field = value
            init()
        }
    //描边样式
    var strokeJoin = Paint.Join.MITER
        set(value) {
            field = value
            init()
        }
    //起始背景角度
    var startBgAngle = -90f
        set(value) {
            field = value
            postInvalidate()
        }
    //结束背景角度
    var endBgAngle = startBgAngle + 360f
        set(value) {
            field = value
            postInvalidate()
        }
    //起始角度
    var startAngle = -90f
        set(value) {
            field = value
            postInvalidate()
        }
    //结束角度
    var endAngle = startAngle + 360f
        set(value) {
            field = value
            postInvalidate()
        }
    //活动角度(绘制缓存)
    private var sweepAngle = 0f
    //更新动画时长
    var duration = 250L
        set(value) {
            field = value
            va.duration = value
            postInvalidate()
        }
    //起始进度(最小进度)
    var startProgress = 0f
        set(value) {
            field = value
            tempProgress = startProgress
            progress = progress
        }
    //结束进度(最大进度)
    var endProgress = 100f
        set(value) {
            field = value
            tempProgress = startProgress
            progress = progress
        }
    //目标进度
    var progress = 50f
        set(value) {
            //合法性检查
            field = when {
                value in startProgress..endProgress -> value

                value > Math.max(startProgress, endProgress) -> Math.max(startProgress, endProgress)

                else -> Math.min(startProgress, endProgress)
            }
            va.setFloatValues(tempProgress, progress)
            va.start()
        }
    //刻度宽度
    var scaleWidth = 0
        set(value) {
            field = value
            postInvalidate()
        }
    //开始结束进度块宽度
    var startEndProgressBlockWidth = 0
        set(value) {
            field = value
            postInvalidate()
        }
    //进度块宽度
    var progressBlockWidth = 0
        set(value) {
            field = value
            postInvalidate()
        }
    //插值器
    private val va = ValueAnimator()
    //画笔
    private val bgPaint = Paint()
    private val forePaint = Paint()
    private val scalePaint = Paint()
    //绘制实时进度
    private var tempProgress = progress
    //绘制矩形
    private val rectF = RectF()

    constructor(ctx: Context) : super(ctx)

    constructor(ctx: Context, attrs: AttributeSet?) : this(ctx, attrs, 0)

    constructor(ctx: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(ctx, attrs, defStyleAttr) {
        //属性配置
        val ta = context.obtainStyledAttributes(attrs, R.styleable.RingProgressView)
        ringColor = ta.getColor(R.styleable.RingProgressView_f_ringColor, ringColor)
        ringBgColor = ta.getColor(R.styleable.RingProgressView_f_ringBgColor, ringBgColor)
        startColor = ta.getColor(R.styleable.RingProgressView_f_startColor, startColor)
        centerColor = ta.getColor(R.styleable.RingProgressView_f_centerColor, centerColor)
        endColor = ta.getColor(R.styleable.RingProgressView_f_endColor, endColor)
        //渐变色设置检查
        if (startColor != Color.TRANSPARENT || centerColor != Color.TRANSPARENT || endColor != Color.TRANSPARENT) {
            useGradient = true
        }
        useGradient = ta.getBoolean(R.styleable.RingProgressView_f_useGradient, useGradient)
        startAngle = ta.getFloat(R.styleable.RingProgressView_f_startAngle, startAngle)
        endAngle = ta.getFloat(R.styleable.RingProgressView_f_endAngle, endAngle)
        startBgAngle = ta.getFloat(R.styleable.RingProgressView_f_startBgAngle, startBgAngle)
        endBgAngle = ta.getFloat(R.styleable.RingProgressView_f_endBgAngle, endBgAngle)
        roundWidth = ta.getDimensionPixelSize(R.styleable.RingProgressView_f_roundWidth, roundWidth)
        strokeCap = when (ta.getInteger(R.styleable.RingProgressView_f_strokeCap, 0)) {
            0 -> Paint.Cap.BUTT
            1 -> Paint.Cap.ROUND
            else -> Paint.Cap.SQUARE
        }
        strokeJoin = when (ta.getInteger(R.styleable.RingProgressView_f_strokeJoin, 0)) {
            0 -> Paint.Join.MITER
            1 -> Paint.Join.ROUND
            else -> Paint.Join.BEVEL
        }
        duration = ta.getInt(R.styleable.RingProgressView_f_duration, duration.toInt()).toLong()
        startProgress = ta.getFloat(R.styleable.RingProgressView_f_startProgress, startProgress)
        endProgress = ta.getFloat(R.styleable.RingProgressView_f_endProgress, endProgress)
        progress = ta.getFloat(R.styleable.RingProgressView_f_progress, progress)
        scaleWidth = ta.getDimensionPixelSize(R.styleable.RingProgressView_f_scaleWidth,scaleWidth)
        startEndProgressBlockWidth = ta.getDimensionPixelSize(R.styleable.RingProgressView_f_s_e_ProgressBlock,startEndProgressBlockWidth)
        progressBlockWidth = ta.getDimensionPixelSize(R.styleable.RingProgressView_f_progressBlock,progressBlockWidth)
        tempProgress = progress
        ta.recycle()
    }

    init {
        va.interpolator = LinearInterpolator()
        va.setFloatValues(startProgress, progress)
        va.duration = duration
        va.addUpdateListener {
            tempProgress = it.animatedValue as Float
            postInvalidate()
        }
        va.start()
    }

    override fun onDraw(canvas: Canvas) {
        //画背景环形
        canvas.drawArc(rectF, startBgAngle, endBgAngle - startBgAngle, false, bgPaint)
        //计算前景环形角度
        sweepAngle = tempProgress / (endProgress - startProgress) * (endAngle - startAngle)
        //画前景环形
        canvas.drawArc(rectF, startAngle, sweepAngle, false, forePaint)

        if(scaleWidth > 0f) {
            //绘制刻度
            // 顺时针时:startAngle < endAngle才有效
            // startEndProgressBlockWidth:开始结束进度块宽度
            // progressBlockWidth : 进度块宽度
            var start = startAngle + startEndProgressBlockWidth
            val p = ((Math.abs(endAngle - startAngle) - 2 * startEndProgressBlockWidth) / (scaleWidth + progressBlockWidth)).toInt()
            for (i in 1..(p + 1)) {
                canvas.drawArc(rectF, start, scaleWidth.toFloat(), false, scalePaint) // 绘制间隔快
                start += scaleWidth + progressBlockWidth
            }
        }
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        if (!init) {
            init = true
        }
        init()
    }

    /**
     * 初始化参数
     */
    private fun init() {
        //设置画笔参数
        //背景画笔属性
        bgPaint.strokeWidth = roundWidth.toFloat()
        bgPaint.strokeCap = strokeCap
        bgPaint.strokeJoin = Paint.Join.ROUND
        bgPaint.style = Paint.Style.STROKE
        bgPaint.isAntiAlias = true
        bgPaint.color = ringBgColor
        //前景画笔属性
        forePaint.strokeWidth = roundWidth.toFloat()
        forePaint.strokeCap = strokeCap
        forePaint.strokeJoin = Paint.Join.ROUND
        forePaint.style = Paint.Style.STROKE
        forePaint.isAntiAlias = true
        //刻度画笔属性
        scalePaint.strokeWidth = roundWidth.toFloat()+1f  // 黑色底, 有去白边
        scalePaint.strokeCap = strokeCap
        scalePaint.strokeJoin = Paint.Join.ROUND
        scalePaint.style = Paint.Style.STROKE
        scalePaint.isAntiAlias = true
        scalePaint.color = Color.BLACK

        //着色器颜色筛选
        val filter = intArrayOf(startColor, centerColor, endColor).filter { it != Color.TRANSPARENT }
        var colorArray: IntArray = when {
            filter.isEmpty() -> intArrayOf(startColor, centerColor, endColor)
            filter.size > 1 -> filter.toIntArray()
            else -> {
                intArrayOf(filter.first(), filter.first())
            }
        }

        //着色器/颜色设置
        if (useGradient) {
            //如果没有渐变色(都是透明 = 0)
            var isNotHaveGraditent = false
            colorArray.forEach aaa@qq.com{
                if (it != 0) {
                    isNotHaveGraditent = false
                    aaa@qq.com
                }
                isNotHaveGraditent = true
            }
            //赋值默认进度条渐变颜色
            if(isNotHaveGraditent){
                colorArray = intArrayOf(defStartColor,defCenterColor,defendColor)
            }
            forePaint.shader =
                    LinearGradient(
                            0f,
                            0f,
                            width.toFloat(),
                            height.toFloat(),
                            colorArray,
                            null,
//                            floatArrayOf(0f,0.45f,0.9f),
                            Shader.TileMode.CLAMP
                    )

        } else {
            forePaint.shader = null
            forePaint.color = ringColor
        }

        //矩形
        rectF.set(
                0f + roundWidth / 2,
                0f + roundWidth / 2,
                width.toFloat() - roundWidth / 2,
                height.toFloat() - roundWidth / 2
        )
        if (init) {
            postInvalidate()
        }
    }
}

自定义属性 

<declare-styleable name="RingProgressView">
        <!--前景环形是否使用渐变-->
        <attr format="boolean" name="f_useGradient"/>
        <!--前景环形默认渐变起始颜色-->
        <attr format="color" name="f_def_startColor"/>
        <!--前景环形默认渐变结束颜色-->
        <attr format="color" name="f_def_endColor"/>
        <!--前景环形渐变起始颜色-->
        <attr format="color" name="f_startColor"/>
        <!--前景环形渐变中间颜色-->
        <attr format="color" name="f_centerColor"/>
        <!--前景环形渐变结束颜色-->
        <attr format="color" name="f_endColor"/>
        <!--前景环形颜色-->
        <attr format="color" name="f_ringColor"/>
        <!--背景环形颜色-->
        <attr format="color" name="f_ringBgColor"/>
        <!--前景环形起始角度-->
        <attr format="float" name="f_startAngle"/>
        <!--前景环形结束角度-->
        <attr format="float" name="f_endAngle"/>
        <!--背景环形起始角度-->
        <attr format="float" name="f_startBgAngle"/>
        <!--背景环形结束角度-->
        <attr format="float" name="f_endBgAngle"/>
        <!--环形线宽-->
        <attr format="dimension" name="f_roundWidth"/>
        <!--进度变化动画时长-->
        <attr format="integer" name="f_duration"/>
        <!--前景环形开始进度-->
        <attr format="float" name="f_startProgress"/>
        <!--前景环形结束进度-->
        <attr format="float" name="f_endProgress"/>
        <!--前景环形目标进度(你想设置的)-->
        <attr format="float" name="f_progress"/>
        <!--刻度宽度-->
        <attr format="dimension" name="f_scaleWidth"/>
        <!--开始结束进度块宽度-->
        <attr format="dimension" name="f_s_e_ProgressBlock"/>
        <!--进度块宽度-->
        <attr format="dimension" name="f_progressBlock"/>
        <!--环形线帽样式Paint.Cap-->
        <attr format="enum" name="f_strokeCap">
            <!--默认没有-->
            <enum name="butt" value="0"/>
            <!--圆角-->
            <enum name="round" value="1"/>
            <!--直角-->
            <enum name="square" value="2"/>
        </attr>
        <!--环形描边样式,详见Paint.Join-->
        <attr format="enum" name="f_strokeJoin">
            <enum name="miter" value="0"/>
            <enum name="round" value="1"/>
            <enum name="bevel" value="2"/>
        </attr>
    </declare-styleable>

 使用:

 <com.android.smartbath.view.CalibrationCircleView
        android:id="@+id/ring2"
        android:layout_width="140px"
        android:layout_height="140px"
        android:layout_centerInParent="true"

        app:f_strokeCap="butt"

        app:f_ringBgColor="#E6999999"

        app:f_useGradient="true"
        app:f_startColor="#017fd6"
        app:f_endColor="#ab36bb"

        app:f_roundWidth="28px"

        app:f_progress="160"
        app:f_endProgress="180"

        app:f_startAngle="-225"
        app:f_startBgAngle="-225"

        app:f_endAngle="45"
        app:f_endBgAngle="45"

        app:f_scaleWidth="4px"
        app:f_s_e_ProgressBlock="4px"
        app:f_progressBlock="4px"
        />

 

相关标签: Android