Home > other >  How to rotate a composable and add a progress listener to the rotation?
How to rotate a composable and add a progress listener to the rotation?

Time:10-27

I am trying to convert my View based code to Compose. I have a composable which takes an image (Painter) as argument and displays it using Image composable. What I want is that whenever the argument value changes, my Image should do a 360 degree rotation and the image should change while angle is approx. 180 degree (i.e. mid-way in the animation)

This is the composable I made.

@Composable
fun MyImage(displayImage: Painter) {
    Image(
        painter = displayImage,
        contentDescription = null,
        modifier = Modifier
            .size(36.dp)
            .clip(CircleShape)
    )
}

Right now when the displayImage changes, the new image is displayed immediately without any animation (obviously). How can I achieve the desired animation?

The code that I am trying to convert looks like this:

fun onImageChange(imageRes: Int) {
    ObjectAnimator.ofFloat(imageView, View.ROTATION, 0f, 360f)
        .apply {
            addUpdateListener {
                if (animatedFraction == 0.5f) {
                    imageView.setImageResource(imageRes)
                }
            }
            start()
        }
}

CodePudding user response:

It can be done using Animatable.

Compose animations are based on coroutines, so you can wait for the animateTo suspend function to complete, change the image and run another animation. Here's a basic example:

var flag by remember { mutableStateOf(true) }
val resourceId = remember(flag) { if (flag) R.drawable.profile else R.drawable.profile_inverted }
val rotation = remember { Animatable(0f) }
val scope = rememberCoroutineScope()

Column(Modifier.padding(30.dp)) {
    Button(onClick = {
        scope.launch {
            rotation.animateTo(
                targetValue = 180f,
                animationSpec = tween(1000, easing = LinearEasing)
            )
            flag = !flag
            rotation.animateTo(
                targetValue = 360f,
                animationSpec = tween(1000, easing = LinearEasing)
            )
            rotation.snapTo(0f)
        }
    }) {
        Text("Rotate")
    }
    Image(
        painterResource(id = resourceId),
        contentDescription = null,
        modifier = Modifier
            .size(300.dp)
            .rotate(rotation.value)
    )
}

Output:

If you want to animate the changing images, you have to put two images in a Box and animate the opacity of both as they rotate using one more Animatable.

  • Related