自定义圆环进度条

 

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() {
        // 禁止硬件加速,硬件加速会有一些问题,这里禁用掉
        setLayerType(LAYER_TYPE_SOFTWARE, null)
        //设置画笔参数
        //背景画笔属性
        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()+2f  //去白边
        scalePaint.alpha = 0
        scalePaint.setXfermode(PorterDuffXfermode(PorterDuff.Mode.DST_IN)) //透明了背景 改法:先把path绘制到一个bitmap上,然后在onDraw中把那个bitmap绘制出来。
        scalePaint.isAntiAlias = true
        scalePaint.strokeCap = strokeCap
        scalePaint.isDither = true
        scalePaint.strokeJoin = Paint.Join.ROUND
        scalePaint.style = Paint.Style.STROKE
//        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 continuing@{
                if (it != 0) {
                    isNotHaveGraditent = false
                    return@continuing
                }
                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"
        />

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值