Search code examples
androidkotlinandroid-jetpack-composeandroid-jetpack-compose-animation

How to measure height and apply in view jetpack compose


I want to measure the height of parent view and animate accordingly to that. I tried from this answer. I think it returns 0.dp to me. So what is the proper way of measuring the item?

Actually I want to animate a view, but my view is 0.dp when I run the application.

@Preview(showBackground = true)
@Composable
fun MoveText() {
    val density = LocalDensity.current
    var columnHeightDp by remember {
        mutableStateOf(0.dp)
    }
    var visible by remember { mutableStateOf(true) }
    val iconOffsetAnimation: Dp by animateDpAsState(
        if (visible) 13.dp else 0.dp, tween(1000)
    )
    val textOffsetAnimation: Dp by animateDpAsState(
        if (visible) 6.dp else 0.dp, tween(1000)
    )
    val viewAlpha: Float by animateFloatAsState(
        targetValue = if (visible) 1f else 0f, animationSpec = tween(
            durationMillis = 1000,
        )
    )
    val heightInDp: Dp by animateDpAsState(
        targetValue = if (visible) {
            columnHeightDp
        } else {
            0.dp
        }, animationSpec = tween(
            durationMillis = 1000,
        )
    )
    ScrollComposeTheme {
        Column(
            modifier = Modifier
                .fillMaxWidth()
                .padding(start = 16.dp, top = 16.dp)
        ) {
            Column(modifier = Modifier
                .onGloballyPositioned { coordinates ->
                    with(density) {
                        columnHeightDp = coordinates.size.height.toDp()
                    }
                }
                .height(heightInDp)
                .background(Color.LightGray)) {
                Image(
                    modifier = Modifier.padding(top = iconOffsetAnimation),
                    alpha = viewAlpha,
                    imageVector = Icons.Default.ShoppingCart,
                    contentDescription = null,
                )
                Text(
                    modifier = Modifier.padding(top = textOffsetAnimation),
                    text = "Hello, Anna",
                    fontSize = 20.sp,
                    color = Color.Black.copy(alpha = viewAlpha),
                )
            }
            Button(
                modifier = Modifier.padding(top = 10.dp),
                onClick = {
                    visible = !visible
                },
            ) {
                Text(text = "Move Text")
            }
        }
    }
}

Actual Output

enter image description here

Expected Output

enter image description here


Solution

  • I tried to achieve something like this demo video from your question.

     val modifier: Modifier = Modifier.background(Color.LightGray)      
     Column(modifier = if (columnHeightDp != 0.dp) {
                    modifier
                        .height(heightInDp)
                } else {
                    modifier.onSizeChanged {
                        with(density) {
                            columnHeightDp = it.height.toDp()
                        }
                    }.wrapContentHeight()
                },
            ) {
             // your content goes here...Image and Text
               }
    
    

    Let me explain how i reach to this.

    modifier = modifier.onSizeChanged {
                    with(density) {
                        columnHeightDp = it.height.toDp()
                    }
                }.wrapContentHeight(),
    

    Here for the dynamic height calculation onSizeChanged method works well. So i save this in a variable as per your need. The wrapContentHeight method in modifier is equivalent as WRAP_CONTENT in xml. It will work nicely showing both components (Image and Text)(Button). But we need the animation so what we need to do is.

    if (columnHeightDp != 0.dp) {
                    modifier
                        .height(heightInDp)
                }
    

    This will apply the animation for the entire Column for Image and Text