Skip to content

Instantly share code, notes, and snippets.

@alexjlockwood
Last active September 18, 2020 04:08
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save alexjlockwood/536dcf7e781b0defd6e17d0d5ffe345d to your computer and use it in GitHub Desktop.
Save alexjlockwood/536dcf7e781b0defd6e17d0d5ffe345d to your computer and use it in GitHub Desktop.
Example implementation of a CircularProgressIndicator using Jetpack Compose https://twitter.com/alexjlockwood/status/1300599202448199681
package com.alexjlockwood.circularprogressindicator
import android.os.Bundle
import android.view.animation.PathInterpolator
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.animation.core.*
import androidx.compose.animation.transition
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.StrokeJoin
import androidx.compose.ui.graphics.vector.VectorAssetBuilder
import androidx.compose.ui.graphics.vector.addPathNodes
import androidx.compose.ui.graphics.vector.group
import androidx.compose.ui.platform.setContent
import androidx.compose.ui.unit.dp
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MaterialTheme {
CircularProgressIndicator(Modifier.fillMaxWidth().fillMaxHeight())
}
}
}
}
@Composable
private fun CircularProgressIndicator(modifier: Modifier = Modifier) {
val state = transition(
definition = CircularProgressIndicatorTransition,
initState = 0,
toState = 1,
)
Image(
asset = VectorAssetBuilder(
defaultWidth = 48.dp,
defaultHeight = 48.dp,
viewportWidth = 48f,
viewportHeight = 48f,
).group(
translationX = 24f,
translationY = 24f,
rotate = state[RotationProp],
) {
addPath(
pathData = remember { addPathNodes("m 0 -18 a 18 18 0 1 1 0 36 a 18 18 0 1 1 0 -36") },
stroke = SolidColor(MaterialTheme.colors.primary),
strokeLineCap = StrokeCap.Square,
strokeLineJoin = StrokeJoin.Miter,
strokeLineWidth = 4f,
trimPathStart = state[TrimPathStartProp],
trimPathEnd = state[TrimPathEndProp],
trimPathOffset = state[TrimPathOffsetProp],
)
}.build(),
modifier = modifier,
)
}
private val TrimPathStartProp = FloatPropKey()
private val TrimPathEndProp = FloatPropKey()
private val TrimPathOffsetProp = FloatPropKey()
private val RotationProp = FloatPropKey()
private val TrimPathStartEasing = PathEasing(android.graphics.Path().apply {
lineTo(0.5f, 0f)
cubicTo(0.7f, 0f, 0.6f, 1f, 1f, 1f)
})
private val TrimPathEndEasing = PathEasing(android.graphics.Path().apply {
cubicTo(0.2f, 0f, 0.1f, 1f, 0.5f, 0.96f)
cubicTo(0.96f, 0.96f, 1f, 1f, 1f, 1f)
})
private class PathEasing(path: android.graphics.Path) : Easing {
private val pathInterpolator = PathInterpolator(path)
override fun invoke(fraction: Float) = pathInterpolator.getInterpolation(fraction)
}
private val CircularProgressIndicatorTransition = transitionDefinition<Int> {
state(0) {
this[TrimPathStartProp] = 0f
this[TrimPathEndProp] = 0.03f
this[TrimPathOffsetProp] = 0f
this[RotationProp] = 0f
}
state(1) {
this[TrimPathStartProp] = 0.75f
this[TrimPathEndProp] = 0.78f
this[TrimPathOffsetProp] = 0.25f
this[RotationProp] = 720f
}
transition(fromState = 0, toState = 1) {
TrimPathStartProp using repeatable(
iterations = AnimationConstants.Infinite,
animation = tween(durationMillis = 1333, easing = TrimPathStartEasing)
)
TrimPathEndProp using repeatable(
iterations = AnimationConstants.Infinite,
animation = tween(durationMillis = 1333, easing = TrimPathEndEasing)
)
TrimPathOffsetProp using repeatable(
iterations = AnimationConstants.Infinite,
animation = tween(durationMillis = 1333, easing = LinearEasing)
)
RotationProp using repeatable(
iterations = AnimationConstants.Infinite,
animation = tween(durationMillis = 4444, easing = LinearEasing)
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment