动态设置百分比
btn1.setOnClickListener { childRate.setFraction(0.23f, true) }
xml中引用控件,这里可以通过app:xxxx,使用所有在style中指定的属性
<com.xxxxxxxxxx.widget.view.GradientCircleView
android:id="@+id/childRate"
android:layout_width="0dp"
android:layout_height="130dp"
app:tipText="正确度"
android:layout_marginTop="32dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/averageRate"
app:layout_constraintTop_toBottomOf="@id/stepPracticeRate"/>
res/values/attrs.xml 定义相应的style属性
<!-- 自定义圆环 -->
<declare-styleable name="GradientCircleView">
<attr name="fromColor" format="color" />
<attr name="toColor" format="color" />
<attr name="shadowColor"/>
<attr name="defaultColor" format="color" />
<attr name="fractionTextColor" format="color" />
<attr name="fractionTextSize" format="dimension"/>
<attr name="tipText" />
<attr name="tipTextColor" />
<attr name="tipTextSize" />
<attr name="outerCircleSize" format="dimension"/>
<attr name="circleWidth" format="dimension" />
</declare-styleable>
<attr name="shadowColor" format="color" />
<attr name="tipText" format="string" />
<attr name="tipTextColor" format="color" />
<attr name="tipTextSize" format="dimension" />
自定义圆环 GradientCircleView
可以在xml中指定相关属性,也可以开放api,用于指定属性
class GradientCircleView @JvmOverloads constructor(context: Context, attributeSet: AttributeSet? = null, defStyle: Int = 0)
: View(context, attributeSet, defStyle) {
private var fromColor = 0 //圆环起始颜色
private var toColor = 0 //圆环结束颜色
private var shadowColor = 0 //圆环外阴影颜色
private var defaultColor = 0 //圆环默认颜色
private var maskFilter: MaskFilter? = null //设置外阴影
//百分比
val numberFormat = NumberFormat.getPercentInstance()
private var fraction = 0f
private var fractionText = ""
private var fractionTextColor = 0
private var fractionTextSize = 0f
private var fractionSuffixSize = 0f
//文案提示
private var tipText = ""
private var tipTextColor = 0
private var tipTextSize = 0f
//圆的大小
private var innerRadius = 0f
private var outerRadius = 0f
private var circleWidth = 0f
private var centerX = 0f
private var centerY = 0f
private var rect = RectF()
var mPaint: Paint = Paint()
var colorGradient: Shader? = null
init {
val a = context.theme.obtainStyledAttributes(attributeSet, R.styleable.GradientCircleView, defStyle, 0)
fromColor = a.getColor(R.styleable.GradientCircleView_fromColor, 0xFF95CAFF.toInt())
toColor = a.getColor(R.styleable.GradientCircleView_toColor, 0xFF5CABFF.toInt())
shadowColor = a.getColor(R.styleable.GradientCircleView_shadowColor, 0xa065AFFF.toInt())
defaultColor = a.getColor(R.styleable.GradientCircleView_defaultColor, 0xFFF2F2F2.toInt())
fractionTextColor = a.getColor(R.styleable.GradientCircleView_fractionTextColor, toColor)
fractionTextSize = a.getDimension(R.styleable.GradientCircleView_fractionTextSize, DensityUtil.sp2px(32f).toFloat())
fractionSuffixSize = DensityUtil.sp2px(18f).toFloat()
tipText = a.getString(R.styleable.GradientCircleView_tipText) ?: ""
tipTextColor = a.getColor(R.styleable.GradientCircleView_tipTextColor, 0xFF666666.toInt())
tipTextSize = a.getDimension(R.styleable.GradientCircleView_tipTextSize, DensityUtil.sp2px(12f).toFloat())
circleWidth = a.getDimension(R.styleable.GradientCircleView_circleWidth, DensityUtil.dp2px(8f).toFloat())
a.recycle()
setLayerType(View.LAYER_TYPE_SOFTWARE, mPaint)
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
// "w $w h $h oldw $oldw oldh $oldh".log()
outerRadius = Math.min(w, h) / 2f - circleWidth
innerRadius = outerRadius - circleWidth / 2f
centerX = w / 2f
centerY = h / 2f
rect.set(centerX - innerRadius, centerY - innerRadius, centerX + innerRadius, centerY + innerRadius)
maskFilter = BlurMaskFilter(circleWidth, BlurMaskFilter.Blur.OUTER)
}
fun setFraction(fraction: Float, colorChangedByFraction: Boolean = false) {
this.fraction = fraction
this.fractionText = (fraction * 100).toInt().toString()
if (colorChangedByFraction) {
when {
fraction < 0.5 -> {
fromColor = 0xFFFF9595.toInt()
toColor = 0xFFFF5C5C.toInt()
shadowColor = 0xa0ff6060.toInt()
fractionTextColor = toColor
}
else -> {
fromColor = 0xFF59F09C.toInt()
toColor = 0xFF2CD276.toInt()
shadowColor = 0xa030D579.toInt()
fractionTextColor = toColor
}
}
}
// "fraction: $fraction centerX:$centerX centerY:$centerY innerRadius:$innerRadius outerRadius:$outerRadius circleWidth:$circleWidth ".log()
colorGradient = SweepGradient(centerX, centerY, fromColor, toColor)
colorGradient = LinearGradient(centerX, centerY - innerRadius, centerX, centerY + innerRadius, fromColor, toColor, Shader.TileMode.MIRROR)
invalidate()
}
override fun onDraw(canvas: Canvas) {
//灰色背景圆环
mPaint.shader = null
mPaint.clearShadowLayer()
mPaint.style = Paint.Style.STROKE
mPaint.isAntiAlias = true
mPaint.strokeWidth = circleWidth
mPaint.color = defaultColor
canvas.drawCircle(centerX, centerY, innerRadius, mPaint)
//分数比例
mPaint.style = Paint.Style.FILL
mPaint.color = fractionTextColor
mPaint.textSize = fractionTextSize
mPaint.typeface = Typeface.DEFAULT_BOLD
val halfTextWidth = mPaint.measureText(fractionText) / 2f
val gap = mPaint.measureText(".")
canvas.drawText(fractionText, centerX - halfTextWidth - gap, centerY, mPaint)
//绘制百分比
mPaint.textSize = fractionSuffixSize
canvas.drawText("%", centerX + halfTextWidth - gap, centerY, mPaint)
//绘制tips
mPaint.color = tipTextColor
mPaint.textSize = tipTextSize
mPaint.typeface = Typeface.DEFAULT
canvas.drawText(tipText, centerX - mPaint.measureText(tipText) / 2f, 1.4f * centerY, mPaint)
//绘制圆弧
mPaint.style = Paint.Style.STROKE
mPaint.strokeCap = Paint.Cap.ROUND
mPaint.shader = colorGradient
// mPaint.maskFilter = maskFilter
mPaint.setShadowLayer(12f, 0f, 6f, shadowColor)
canvas.drawArc(rect, -90f, fraction * 360, false, mPaint)
// "draw sweepAngel${fraction * 360} ".log()
}
}