androidkotlinandroid-jetpack-composeandroid-jetpackjetpack

Android jetpack compose countdown animation


Is it possible to make an animation like gif with Jetpack Compose?

enter image description here


Solution

  • One of the ways to do is using Animatables that animate scale and alpha after each other.

    enter image description here

    create a class that contains 2 Animatables

    class AnimatedCountdownTimer(
        private val coroutineScope: CoroutineScope
    ) {
    
        private val animatableScale = Animatable(1f)
        private val animatableAlpha = Animatable(1f)
    
        val scale: Float
            get() = animatableScale.value
    
        val alpha: Float
            get() = animatableAlpha.value
    
        fun start(initialValue: Int, endValue: Int, onChange: (Int) -> Unit) {
    
            var value = initialValue
    
            coroutineScope.launch {
                while (value > endValue - 1) {
                    onChange(value)
                    animatableScale.snapTo(1f)
                    animatableAlpha.snapTo(1f)
                    animatableScale.animateTo(2f, animationSpec = tween(750))
                    animatableAlpha.animateTo(0f, animationSpec = tween(250))
                    value--
    
                }
            }
        }
    }
    

    And use it as

    @Preview
    @Composable
    fun TimerTest() {
    
    
        var timer by remember {
            mutableStateOf(5)
        }
    
        val coroutineScope = rememberCoroutineScope()
    
        val animatedCountdownTimer = remember {
            AnimatedCountdownTimer(coroutineScope)
        }
    
        Column(
            modifier = Modifier.fillMaxSize().padding(16.dp),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Text(
                modifier = Modifier.graphicsLayer {
                    scaleX = animatedCountdownTimer.scale
                    scaleY = animatedCountdownTimer.scale
                    alpha = animatedCountdownTimer.alpha
                },
                text = "$timer",
                fontSize = 120.sp,
                fontWeight = FontWeight.Bold,
                color = Color.Gray
            )
            Spacer(Modifier.height(20.dp))
            Button(
                modifier = Modifier.fillMaxWidth(),
                onClick = {
                    animatedCountdownTimer.start(5, 0) {
                        timer = it
                    }
                }
            ) {
                Text("Start")
            }
        }
    }
    

    If you don't want to scale to 1f after animation end just ad if block so it can stay at scale. Any param can be customized easily time durations or max scale.

    If you wish to keep last number scaled you can use it like this

      coroutineScope.launch {
            while (value > endValue - 1) {
                onChange(value)
                animatableScale.snapTo(1f)
                animatableAlpha.snapTo(1f)
                animatableScale.animateTo(2f, animationSpec = tween(750))
                if (value > endValue) {
                    animatableAlpha.animateTo(0f, animationSpec = tween(250))
                }
                value--
            }
        }