0

I am a newbie in android Kotlin development, it would be really appreciable, if someone can help me out. I already spent more than a week on the research of jetpack compose animation.

I have been trying to create a control where the circle has to given a pulsating animation and that needs to be controlled with various states or configuration. As a note - the animation is not only for a single circle, it has to be with multiple circle for different states.

I was able to implement the required animation with infiniteTransition, but that that doesn’t meet my requirement as it animates infinitely. I have given the code that I tried.

            @Composable
fun InfinitelyFlowingCircles() {
    val primaryColor = MaterialTheme.colors.primary
    val midCircle = primaryColor.copy(0.50f)

    DrawCircleOnCanvas(
        scale = scaleInfiniteTransition(initialValue = 0.5f, targetValue = 1f, durationMillis = 1000),
        color = midCircle,
        radiusRatio = 2f
    )
}

@Composable
private fun DrawCircleOnCanvas(
    scale: Float,
    color: Color,
    radiusRatio: Float
) {
    Canvas(
        modifier = Modifier
            .fillMaxSize()
            .graphicsLayer {
                scaleX = scale
                scaleY = scale
            }
    ) {
        val canvasWidth = size.width
        val canvasHeight = size.height
        drawCircle(
            color = color,
            center = Offset(
                x = canvasWidth / 2,
                y = canvasHeight / 2
            ),
            radius = size.minDimension / radiusRatio,
        )
    }
}

@Composable
private fun scaleInfiniteTransition(
    initialValue: Float = 0f,
    targetValue: Float,
    durationMillis: Int,
): Float {
    val infiniteTransition = rememberInfiniteTransition()
    val scale: Float by infiniteTransition.animateFloat(
        initialValue = initialValue,
        targetValue = targetValue,
        animationSpec =  infiniteRepeatable(
            animation = tween(durationMillis, easing = LinearEasing),
            repeatMode = RepeatMode.Reverse
        )
    )
    return scale
}

Also, I have tried a finite animation which works well, but I could find an option to get the finishListener in that animation so that I couldn't find a way to start the next animation. The code for the same has mentioned below.

   @Composable
fun UpdateTransition() {
    var circleState by remember {
        mutableStateOf(CircleState.Small)
    }

    val transition = updateTransition(
        targetState = circleState, label =
        "Circle Transition"
    )

    val color by transition.animateColor(label = "Color") { state ->
        when (state) {
            CircleState.Small -> Color.Blue
            CircleState.Large -> Color.Red
        }
    }

    val size by transition.animateDp(label = "Size") { state ->
        when (state) {
            CircleState.Small -> 100.dp
            CircleState.Large -> 200.dp
        }
    }
        Box(
            Modifier
                .clip(CircleShape)
                .size(size)
                .background(color)
                .clickable(
                    onClick = {
                        circleState = when (circleState) {
                            CircleState.Small -> CircleState.Large
                            CircleState.Large -> CircleState.Small
                        }
                    })
        ) {
        }
}
Kotlin Learner
  • 3,995
  • 6
  • 47
  • 127
sree_iphonedev
  • 3,514
  • 6
  • 31
  • 50
  • See the output in this post: https://semicolonspace.com/jetpack-compose-infinitetransition/ Is this what you are looking for? Instead of heart, you want circle? – SemicolonSpace Aug 09 '22 at 11:45
  • Thanks for the quick response. The sample you showed is infinite transition, but I look for finite transition with 1 repeat and I want to get a callback on the completion of animation. Is there anyway that I can achieve this. Thanks – sree_iphonedev Aug 09 '22 at 13:29

1 Answers1

2

You could use Animatable.

import androidx.compose.animation.core.Animatable
// Not the androidx.compose.animation.Animatable

@Composable
private fun MyUI() {

    val coroutineScope = rememberCoroutineScope()

    val scale = remember {
        Animatable(initialValue = 1f)
    }

    Box(
        modifier = Modifier
            .scale(scale = scale.value)
            .requiredSize(size = 40.dp)
            .background(color = Color.Red)
            .clickable {
                coroutineScope.launch {
                    scale.animateTo(
                        targetValue = 4f,
                        animationSpec = tween(durationMillis = 3000)
                    )
                    // after 3 seconds, this is executed
                    Log.d("ClickableBlock", "Animation Completed")
                }
            }
    )
}
Stanley Ko
  • 3,383
  • 3
  • 34
  • 60
SemicolonSpace
  • 1,007
  • 11
  • 20
  • Thank you, friend. I will try this and get back to you. I have to have a sequence of animations one after another and that should be configurable. If I have an array of animation configuration to different circles one above the other, how does this coroutine scope has to be updated? Thanks for the quick responses. – sree_iphonedev Aug 09 '22 at 14:13
  • Could this coroutineScope.launch be used outside of .clickable, to be initiated by some if() condition? – Milos Sep 24 '22 at 09:32
  • 1
    Yes, you can use it – SemicolonSpace Sep 24 '22 at 09:59
  • Should I disregard Android Studio's remark "Calls to launch should happen inside a LaunchedEffect and not composition"? – Milos Sep 24 '22 at 13:11