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"
/>