Created
July 13, 2018 07:59
-
-
Save txusballesteros/54d0d857479a593527e9293410bf2d08 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
import android.animation.ValueAnimator | |
import android.content.Context | |
import android.graphics.* | |
import android.util.AttributeSet | |
import android.util.TypedValue | |
import android.view.View | |
import android.view.animation.DecelerateInterpolator | |
class OnboardingPageIndicatorView @JvmOverloads constructor( | |
context: Context, | |
attrs: AttributeSet? = null, | |
defStyle: Int = 0 | |
) : View(context, attrs, defStyle) { | |
companion object { | |
private const val PADDING_IN_DP = 2f | |
private const val MINIMUM_VALUE = 0f | |
private const val MAXIMUM_VALUE = 100f | |
private const val ANIMATION_DURATION_IN_MS = 1000L | |
} | |
private val backgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG) | |
private val progressPaint = Paint(Paint.ANTI_ALIAS_FLAG) | |
private var backgroundLeftCenter: PointF = PointF(0f, 0f) | |
private var backgroundRightCenter: PointF = PointF(0f, 0f) | |
private var progressLeftCenter: PointF = PointF(0f, 0f) | |
private var progressRightCenter: PointF = PointF(0f, 0f) | |
private lateinit var backgroundLeftRect: RectF | |
private lateinit var backgroundRightRect: RectF | |
private lateinit var progressLeftRect: RectF | |
private lateinit var progressRightRect: RectF | |
private lateinit var backgroundPath: Path | |
private lateinit var progressPath: Path | |
private lateinit var animator: ValueAnimator | |
var value: Float = 33.3f | |
set(value) { | |
if (value in MINIMUM_VALUE..MAXIMUM_VALUE) { | |
field = value | |
playAnimation() | |
} | |
} | |
private val padding: Float | |
get() = dp2px(PADDING_IN_DP) | |
private val progressRadious: Float | |
get() = ((measuredHeight.toFloat() - (padding * 2)) / 2f) | |
private val backgroundRadious: Float | |
get() = (measuredHeight.toFloat() / 2f) | |
private val progressOffset: Float | |
get() = (measuredWidth.toFloat() / (MAXIMUM_VALUE - MINIMUM_VALUE)) | |
private val calculatedProgressX: Float | |
get() = ((value * progressOffset) - progressRadious - padding) | |
init { | |
backgroundPaint.color = resources.getColor(R.color.purple) | |
backgroundPaint.style = Paint.Style.FILL | |
progressPaint.color = resources.getColor(R.color.light_purple) | |
progressPaint.style = Paint.Style.FILL | |
} | |
private fun playAnimation() { | |
if (::animator.isInitialized) animator.cancel() | |
val currentCenterX = progressRightCenter.x | |
val newCenterX = calculatedProgressX | |
animator = ValueAnimator.ofFloat(currentCenterX, newCenterX) | |
animator.duration = ANIMATION_DURATION_IN_MS | |
animator.interpolator = DecelerateInterpolator() | |
animator.addUpdateListener { | |
val positionX = (animator.animatedValue as Float) | |
calculateProgressCenters(positionX) | |
invalidate() | |
} | |
animator.start() | |
} | |
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { | |
val width = MeasureSpec.getSize(widthMeasureSpec) | |
val height = MeasureSpec.getSize(heightMeasureSpec) | |
setMeasuredDimension(width, height) | |
calculateBackgroundCenters() | |
calculateBackgroundRects() | |
calculateBackgroundPath() | |
calculateProgressCenters(calculatedProgressX) | |
} | |
private fun calculateBackgroundCenters() { | |
backgroundLeftCenter = PointF(backgroundRadious, backgroundRadious) | |
backgroundRightCenter = PointF(measuredWidth - backgroundRadious, backgroundRadious) | |
} | |
private fun calculateProgressCenters(rightX: Float) { | |
val progressLeftX = (padding + progressRadious) | |
var progressRightX = rightX | |
if (progressRightX < progressLeftX) progressRightX = progressLeftX | |
progressLeftCenter = PointF(progressLeftX, padding + progressRadious) | |
progressRightCenter = PointF(progressRightX, padding + progressRadious) | |
} | |
private fun calculateBackgroundRects() { | |
backgroundLeftRect = RectF( | |
backgroundLeftCenter.x - backgroundRadious, | |
backgroundLeftCenter.y - backgroundRadious, | |
backgroundLeftCenter.x + backgroundRadious, | |
backgroundLeftCenter.y + backgroundRadious | |
) | |
backgroundRightRect = RectF( | |
backgroundRightCenter.x - backgroundRadious, | |
backgroundRightCenter.y - backgroundRadious, | |
backgroundRightCenter.x + backgroundRadious, | |
backgroundRightCenter.y + backgroundRadious | |
) | |
} | |
private fun calculateProgressRects() { | |
progressLeftRect = RectF( | |
progressLeftCenter.x - progressRadious, | |
progressLeftCenter.y - progressRadious, | |
progressLeftCenter.x + progressRadious, | |
progressLeftCenter.y + progressRadious | |
) | |
progressRightRect = RectF( | |
progressRightCenter.x - progressRadious, | |
progressRightCenter.y - progressRadious, | |
progressRightCenter.x + progressRadious, | |
progressRightCenter.y + progressRadious | |
) | |
} | |
private fun calculateBackgroundPath() { | |
backgroundPath = Path() | |
backgroundPath.arcTo(backgroundLeftRect, 90f, 180f) | |
backgroundPath.arcTo(backgroundRightRect, 270f, 180f) | |
backgroundPath.close() | |
} | |
private fun calculateProgressPath() { | |
progressPath = Path() | |
progressPath.arcTo(progressLeftRect, 90f, 180f) | |
progressPath.arcTo(progressRightRect, 270f, 180f) | |
progressPath.close() | |
} | |
override fun onDraw(canvas: Canvas) { | |
calculateProgressRects() | |
calculateProgressPath() | |
canvas.drawPath(backgroundPath, backgroundPaint) | |
canvas.drawPath(progressPath, progressPaint) | |
} | |
private fun dp2px(value: Float): Float = | |
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, resources.displayMetrics) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment