Native implementation of segmented progress bar for android on canvas.
class SegmentedProgressBar(context: Context?, attributeSet: AttributeSet? = null) : View(context, attributeSet) {
private var cornerRadius = 43f // set corner radius for your segmented progress bar
private val paint = Paint()
var strokePaint = Paint()
private var barContexts: List<BarContext> = listOf()
private val segment = RectF(
0f,
0f,
0f,
height.toFloat()
)
override fun onDraw(canvas: Canvas) {
var filling = 0f
cornerRadius = resources.getInteger(R.integer.card_radius).toFloat()
val barContextsLastIndex = barContexts.lastIndex
barContexts.forEachIndexed { i, bar ->
val shader: Shader = LinearGradient(
0f, 0f, 5f, height.toFloat(),
intArrayOf(bar.colorTo, bar.colorFrom), null, TileMode.CLAMP
)
paint.shader = shader
segment.right = width * bar.percentage + filling
segment.left = filling
segment.bottom = height.toFloat()
strokePaint.style = Paint.Style.STROKE
strokePaint.color = Color.BLACK
strokePaint.strokeWidth = 10f
var topLeftRadius = 0f
var bottomLeftRadius = 0f
var topRightRadius = 0f
var bottomRightRadius = 0f
when (i) {
0 -> {
topLeftRadius = cornerRadius
bottomLeftRadius = cornerRadius
}
barContextsLastIndex -> {
topRightRadius = cornerRadius
bottomRightRadius = cornerRadius
}
}
canvas.drawPath(
getPathOfRoundedRectF(
segment,
topLeftRadius = topLeftRadius,
bottomLeftRadius = bottomLeftRadius,
topRightRadius = topRightRadius,
bottomRightRadius = bottomRightRadius
), paint
)
strokePaint.shader = LinearGradient(
0f,
0f,
0f,
height.toFloat(),
intArrayOf(Color.parseColor("#80FFFFFF"), Color.parseColor("#33000000")),
null,
TileMode.CLAMP
)
strokePaint.maskFilter = BlurMaskFilter(18f, BlurMaskFilter.Blur.INNER)
strokePaint.strokeWidth = 12f
canvas.drawPath(
getPathOfRoundedRectF(
segment,
topLeftRadius = topLeftRadius + topLeftRadius * 0.2f,
bottomLeftRadius = bottomLeftRadius + bottomLeftRadius * 0.2f,
topRightRadius = topRightRadius + topRightRadius * 0.2f,
bottomRightRadius = bottomRightRadius + bottomRightRadius * 0.2f,
excludeVertical = true
),
strokePaint
)
filling += width * bar.percentage
}
}
fun setContexts(barContexts: List<BarContext>) {
this.barContexts = barContexts
}
private fun getPathOfRoundedRectF(
rect: RectF,
topLeftRadius: Float = 0f,
topRightRadius: Float = 0f,
bottomRightRadius: Float = 0f,
bottomLeftRadius: Float = 0f,
excludeVertical: Boolean = false
): Path {
val tlRadius = topLeftRadius.coerceAtLeast(0f)
val trRadius = topRightRadius.coerceAtLeast(0f)
val brRadius = bottomRightRadius.coerceAtLeast(0f)
val blRadius = bottomLeftRadius.coerceAtLeast(0f)
with(Path()) {
moveTo(rect.left + tlRadius, rect.top)
//setup top border
lineTo(rect.right - trRadius, rect.top)
//setup top-right corner
arcTo(
RectF(
rect.right - trRadius * 2f,
rect.top,
rect.right,
rect.top + trRadius * 2f
), -90f, 90f
)
if (trRadius != 0f || !excludeVertical) {
//setup right border
lineTo(rect.right, rect.bottom - brRadius)
//setup bottom-right corner
arcTo(
RectF(
rect.right - brRadius * 2f,
rect.bottom - brRadius * 2f,
rect.right,
rect.bottom
), 0f, 90f
)
} else {
moveTo(rect.right, rect.bottom - brRadius)
}
//setup bottom border
lineTo(rect.left + blRadius, rect.bottom)
//setup bottom-left corner
arcTo(
RectF(
rect.left,
rect.bottom - blRadius * 2f,
rect.left + blRadius * 2f,
rect.bottom
), 90f, 90f
)
if (tlRadius != 0f || !excludeVertical) {
//setup left border
lineTo(rect.left, rect.top + tlRadius)
//setup top-left corner
arcTo(
RectF(
rect.left,
rect.top,
rect.left + tlRadius * 2f,
rect.top + tlRadius * 2f
),
180f,
90f
)
} else {
moveTo(rect.left, rect.top + tlRadius)
}
return this
}
}
class BarContext(val colorFrom: Int, val colorTo: Int, val percentage: Float)
}
<com.example.myapplication.view.SegmentedProgressBar
android:id="@+id/segment_progress_bar"
android:layout_width="400dp"
android:layout_height="100dp" />
//findViewById or Binding for your SegmentedProgressBar
segmentProgressBar.setContexts(
barContexts = listOf(
SegmentedProgressBar.BarContext(
Color.parseColor("#FF6C5C"), //gradient start
Color.parseColor("#FF8E95"), //gradient stop
0.35f //percentage for segment
),
SegmentedProgressBar.BarContext(
Color.parseColor("#FFBF74"),
Color.parseColor("#FFDA58"),
0.19f
),
SegmentedProgressBar.BarContext(
Color.parseColor("#C4AEFC"),
Color.parseColor("#F29DDA"),
0.16f
),
SegmentedProgressBar.BarContext(
Color.parseColor("#49C6FC"),
Color.parseColor("#5AE1FF"),
0.30f
),
)
)