Created
November 29, 2018 11:45
-
-
Save saturov/8c18b63e42cb791a129196ec2aa763e3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Виджет "звездного" прогресс-бара. | |
* | |
* Для корректной работы необходимо в рантайме задать значение [@see totalProgress], а затем задать | |
* актуальное значение [@see currentProgress]. | |
* | |
* Четыре цвета градиента задаются через XML-атрибуты: | |
* * [@see ProgressStarView_psv_color_start] | |
* * [@see ProgressStarView_psv_color_middle_1] | |
* * [@see ProgressStarView_psv_color_middle_2] | |
* * [@see ProgressStarView_psv_color_end] | |
*/ | |
class ProgressStarView(context: Context?, attrs: AttributeSet?) : View(context, attrs) { | |
//общий прогресс | |
var totalProgress: Int = 10 | |
set(value) { | |
field = when { | |
value < 0 -> 0 | |
else -> value | |
} | |
invalidate() | |
} | |
//текущий прогресс | |
var currentProgress: Int = 9 | |
set(value) { | |
field = when { | |
value > totalProgress -> totalProgress | |
value < 0 -> 0 | |
else -> value | |
} | |
invalidate() | |
} | |
private var viewWidth: Int = 0 | |
private var viewHeight: Int = 0 | |
//толщина линии контура звезды | |
private var starStrokeWidth: Float = 0F | |
get() = (viewWidth / 24).toFloat() | |
private val paint = Paint() | |
private var gradientColors = intArrayOf() | |
private var linearGradient: LinearGradient? = null | |
private var starStrokePath = Path() | |
private var progressPath = Path() | |
init { | |
obtainAttributes(attrs) | |
//инициализация "кисточки" | |
paint.apply { | |
isAntiAlias = true | |
strokeCap = Paint.Cap.ROUND //закругление окончаний примитивов | |
strokeJoin = Paint.Join.ROUND //закругление узлов сопряжения | |
style = Paint.Style.STROKE //отрисовка границы без заливки | |
} | |
} | |
override fun onDraw(canvas: Canvas?) { | |
canvas ?: return | |
drawStarStroke(canvas) | |
super.onDraw(canvas) | |
} | |
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { | |
super.onMeasure(widthMeasureSpec, heightMeasureSpec) | |
viewWidth = MeasureSpec.getSize(widthMeasureSpec) | |
viewHeight = MeasureSpec.getSize(heightMeasureSpec) | |
prepareGradientFill() | |
} | |
private fun obtainAttributes(attrs: AttributeSet?) { | |
attrs ?: return | |
val ta = context.obtainStyledAttributes(attrs, R.styleable.ProgressStarView) | |
val colorStart = ta.getColor(R.styleable.ProgressStarView_psv_color_start, Color.BLACK) | |
val colorMiddle1 = ta.getColor(R.styleable.ProgressStarView_psv_color_middle_1, Color.BLACK) | |
val colorMiddle2 = ta.getColor(R.styleable.ProgressStarView_psv_color_middle_2, Color.BLACK) | |
val colorEnd = ta.getColor(R.styleable.ProgressStarView_psv_color_end, Color.BLACK) | |
ta.recycle() | |
this.gradientColors = intArrayOf(colorStart, colorMiddle1, colorMiddle2, colorEnd) | |
} | |
/** | |
* Отрисовка контура звезды. | |
*/ | |
private fun drawStarStroke(canvas: Canvas) { | |
paint.strokeWidth = starStrokeWidth | |
paint.color = ContextCompat.getColor(context, R.color.bossanova_light) | |
starStrokePath.moveTo(viewWidth * 0.5f, viewHeight * 0.029f) //1 | |
starStrokePath.quadTo(viewWidth * 0.545f, viewHeight * 0.029f, viewWidth * 0.566f, viewHeight * 0.072f) //2 | |
starStrokePath.lineTo(viewWidth * 0.666f, viewHeight * 0.295f) //3 | |
starStrokePath.lineTo(viewWidth * 0.909f, viewHeight * 0.331f) //4 | |
starStrokePath.quadTo(viewWidth * 0.954f, viewHeight * 0.338f, viewWidth * 0.968f, viewHeight * 0.383f) //5 | |
starStrokePath.quadTo(viewWidth * 0.982f, viewHeight * 0.428f, viewWidth * 0.949f, viewHeight * 0.461f) //6 | |
starStrokePath.lineTo(viewWidth * 0.778f, viewHeight * 0.635f) //7 | |
starStrokePath.lineTo(viewWidth * 0.818f, viewHeight * 0.88f) //8 | |
starStrokePath.quadTo(viewWidth * 0.826f, viewHeight * 0.928f, viewWidth * 0.789f, viewHeight * 0.955f) //9 | |
starStrokePath.quadTo(viewWidth * 0.752f, viewHeight * 0.983f, viewWidth * 0.711f, viewHeight * 0.961f) //10 | |
starStrokePath.lineTo(viewWidth * 0.5f, viewHeight * 0.845f) //11 | |
starStrokePath.lineTo(viewWidth * 0.288f, viewHeight * 0.961f) //12 | |
starStrokePath.quadTo(viewWidth * 0.246f, viewHeight * 0.983f, viewWidth * 0.21f, viewHeight * 0.955f) //13 | |
starStrokePath.quadTo(viewWidth * 0.173f, viewHeight * 0.928f, viewWidth * 0.181f, viewHeight * 0.88f) //14 | |
starStrokePath.lineTo(viewWidth * 0.221f, viewHeight * 0.635f) //15 | |
starStrokePath.lineTo(viewWidth * 0.05f, viewHeight * 0.461f) //16 | |
starStrokePath.quadTo(viewWidth * 0.016f, viewHeight * 0.428f, viewWidth * 0.031f, viewHeight * 0.383f) //17 | |
starStrokePath.quadTo(viewWidth * 0.045f, viewHeight * 0.338f, viewWidth * 0.09f, viewHeight * 0.331f) //18 | |
starStrokePath.lineTo(viewWidth * 0.327f, viewHeight * 0.295f) //19 | |
starStrokePath.quadTo(viewWidth * 0.454f, viewHeight * 0.029f, viewWidth * 0.5f, viewHeight * 0.029f) //20 | |
paint.shader = null | |
canvas.drawPath(starStrokePath, paint) | |
val pathMeasure = PathMeasure(starStrokePath, false) | |
val currentProgressLength = | |
pathMeasure.length * (currentProgress.toFloat() / totalProgress.toFloat()) | |
progressPath.rewind() | |
pathMeasure.getSegment(0f, currentProgressLength, progressPath, true) | |
paint.shader = linearGradient | |
canvas.drawPath(progressPath, paint) | |
} | |
/** | |
* Подготовка градиентной заливки прогресс-бара. | |
*/ | |
private fun prepareGradientFill() { | |
if (linearGradient == null) { | |
linearGradient = LinearGradient( | |
viewWidth * 0.208f, | |
viewWidth * 0.220f, | |
viewWidth.toFloat(), | |
viewHeight.toFloat(), | |
gradientColors, | |
null, | |
Shader.TileMode.CLAMP | |
) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment