Skip to content

Instantly share code, notes, and snippets.

@Bloody-Badboy
Created June 2, 2023 05:56
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 Bloody-Badboy/4f9ff9d601ef0ce4cfaa41cae7f07856 to your computer and use it in GitHub Desktop.
Save Bloody-Badboy/4f9ff9d601ef0ce4cfaa41cae7f07856 to your computer and use it in GitHub Desktop.
package dev.arpan.ui.widget
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
import android.os.Parcel
import android.os.Parcelable
import android.util.AttributeSet
import android.view.View
import androidx.core.graphics.withTranslation
import kotlin.math.cos
import kotlin.math.max
import kotlin.math.min
import kotlin.math.sin
class SalesProgressView @JvmOverloads constructor(
context: Context,
attributeSet: AttributeSet? = null
) : View(context, attributeSet) {
private val rectF = RectF()
private val indicatorRectF = RectF()
private val strokeWidth = 2f.dp
private val indicatorSize = 12f.dp
private val indicatorStrokeWidth = 1f.dp
private val backgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.FILL
color = Color.argb(0x66, 0xFF, 0xFF, 0xFF)
}
private val strokeBackgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.STROKE
strokeWidth = this@SalesProgressView.strokeWidth
strokeCap = Paint.Cap.ROUND
color = Color.WHITE
}
private val strokePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.STROKE
strokeWidth = this@SalesProgressView.strokeWidth
strokeCap = Paint.Cap.ROUND
color = Color.rgb(0xFF, 0x75, 0x41)
}
private val indicatorFillPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.FILL
color = Color.WHITE
}
private val indicatorStrokePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.STROKE
strokeWidth = this@SalesProgressView.indicatorStrokeWidth
color = Color.rgb(0xFF, 0x75, 0x41)
}
var progress: Float = 0f
set(value) {
field = value.coerceAtMost(100f)
invalidate()
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val height = getDefaultSize(suggestedMinimumHeight, heightMeasureSpec)
val width = getDefaultSize(suggestedMinimumWidth, widthMeasureSpec)
val min = min(width, height)
setMeasuredDimension(min, min)
val halfStrokeWidth = strokeWidth / 2f
val halfIndicatorWidth = indicatorSize / 2f
val halfFinal = max(halfStrokeWidth, halfIndicatorWidth)
rectF.set(
halfFinal,
halfFinal,
min - halfFinal,
min - halfFinal
)
val halfIndicatorStrokeWidth = indicatorStrokeWidth / 2f
indicatorRectF.set(
-halfIndicatorWidth + halfIndicatorStrokeWidth,
-halfIndicatorWidth + halfIndicatorStrokeWidth,
halfIndicatorWidth - halfIndicatorStrokeWidth,
halfIndicatorWidth - halfIndicatorStrokeWidth
)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val startAngle = -90f
val angle = 360 * progress / 100f
val radius = rectF.width() / 2f
canvas.drawOval(rectF, backgroundPaint)
canvas.drawOval(rectF, strokeBackgroundPaint)
canvas.drawArc(rectF, startAngle, angle, false, strokePaint)
val x = (radius * cos(Math.toRadians(angle + startAngle * 1.0)).toFloat()) + rectF.centerX()
val y = (radius * sin(Math.toRadians(angle + startAngle * 1.0)).toFloat()) + rectF.centerY()
canvas.withTranslation(x, y) {
canvas.drawOval(indicatorRectF, indicatorFillPaint)
canvas.drawOval(indicatorRectF, indicatorStrokePaint)
}
}
override fun onSaveInstanceState(): Parcelable? {
val superState = super.onSaveInstanceState()
val savedState = SavedState(superState)
savedState.progress = this.progress
return savedState
}
override fun onRestoreInstanceState(state: Parcelable?) {
state as SavedState
super.onRestoreInstanceState(state.superState)
progress = state.progress
}
private class SavedState : BaseSavedState {
var progress: Float = 0F
constructor(parcelable: Parcelable?) : super(parcelable)
private constructor(parcel: Parcel) : super(parcel) {
progress = parcel.readFloat()
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
super.writeToParcel(parcel, flags)
parcel.writeFloat(progress)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<SavedState> {
override fun createFromParcel(parcel: Parcel): SavedState {
return SavedState(parcel)
}
override fun newArray(size: Int): Array<SavedState?> {
return arrayOfNulls(size)
}
}
}
private val Float.dp: Float get() = this * resources.displayMetrics.density
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment