Search code examples
animationandroid-jetpack-compose

How to make Jetpack Compose Image animating infinite


I have an animated-vector drawable. I want this animated vector to be animated in loop while this image is showing. Cannot find a good solution for this.

        val image = animatedVectorResource(R.drawable.no_devices_animated)
        var atEnd by remember { mutableStateOf(false) }
        Image(
            painter = image.painterFor(atEnd),
            "image",
            Modifier.width(150.dp).clickable {
                atEnd = !atEnd
            },
            contentScale = ContentScale.Fit)

When I tap on the image it is animating but then stops. This is kind of an infinite progress.


Solution

  • Leaving my solution here (using compose 1.2.0-alpha07).

    Add the dependencies in your build.gradle

    dependencies {
        implementation "androidx.compose.animation:animation:$compose_version"
        implementation "androidx.compose.animation:animation-graphics:$compose_version"
    }
    

    And do the following:

    @ExperimentalAnimationGraphicsApi
    @Composable
    fun AnimatedVectorDrawableAnim() {
        val image = AnimatedImageVector.animatedVectorResource(R.drawable.avd_anim)
        var atEnd by remember { mutableStateOf(false) }
        // This state is necessary to control start/stop animation
        var isRunning by remember { mutableStateOf(true) }
        // The coroutine scope is necessary to launch the coroutine 
        // in response to the click event
        val scope = rememberCoroutineScope()
        // This function is called when the component is first launched
        // and lately when the button is pressed
        suspend fun runAnimation() {
            while (isRunning) {
                delay(1000) // set here your delay between animations
                atEnd = !atEnd
            }
        }
        // This is necessary just if you want to run the animation when the
        // component is displayed. Otherwise, you can remove it.
        LaunchedEffect(image) {
            runAnimation()
        }
        Image(
            painter = rememberAnimatedVectorPainter(image, atEnd),
            null,
            Modifier
                .size(150.dp)
                .clickable {
                    isRunning = !isRunning // start/stop animation
                    if (isRunning) // run the animation if isRunning is true.
                        scope.launch {
                            runAnimation()
                        }
                },
            contentScale = ContentScale.Fit,
            colorFilter = ColorFilter.tint(Color.Red)
        )
    }
    

    Case you need to repeat the animation from the start, the only way I find was create two vector drawables like ic_start and ic_end using the information declared in the animated vector drawable and do the following:

    // this is the vector resource of the start point of the animation
    val painter = rememberVectorPainter(
        image = ImageVector.vectorResource(R.drawable.ic_start)
    )
    val animatedPainter = rememberAnimatedVectorPainter(
        animatedImageVector = AnimatedImageVector.animatedVectorResource(R.drawable.avd_anim),
        atEnd = !atEnd
    )
    
    Image(
        painter = if (atEnd) painter else animatedPainter,
        ...
    )
    

    So, when the animated vector is at the end position, the static image is drawn. After the delay, the animation is played again. If you need a continuous repetition, set the delay as the same duration of the animation.

    Here's the result: enter image description here