Skip to content

Instantly share code, notes, and snippets.

@saturov
Created November 29, 2018 11:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save saturov/8c18b63e42cb791a129196ec2aa763e3 to your computer and use it in GitHub Desktop.
Save saturov/8c18b63e42cb791a129196ec2aa763e3 to your computer and use it in GitHub Desktop.
/**
* Виджет "звездного" прогресс-бара.
*
* Для корректной работы необходимо в рантайме задать значение [@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