Arash Shahzamani
Arash Shahzamani

Reputation: 457

How to have a layout like this in Jetpack Compose

I wanna have this layout in Jetpack Compose. How can I place the image at the bottom of the card and have it a bit outside the card borders? Also how to make sure that the content above won't collide with the image, because the content above can be longer. Thanks very much.

enter image description here

Upvotes: 3

Views: 2329

Answers (2)

Phil Dukhov
Phil Dukhov

Reputation: 88407

You can use Card for main view layout.

To have an icon placed like this, you need to place it with Card into a box, apply Alignment.BottomCenter alignment to image and add paddings.

To make image be under your text, I've added Spacer with size calculated on image size.

@Composable
fun TestView() {
    CompositionLocalProvider(
        LocalContentColor provides Color.White
    ) {
        Column {
            LazyRow {
                items(10) {
                    Item()
                }
            }
            Box(Modifier.fillMaxWidth().background(Color.DarkGray)) {
                Text("next view")
            }
        }

    }
}

@Composable
fun Item() {
    Box(
        Modifier
            .padding(10.dp)
    ) {
        val imagePainter = painterResource(id = R.drawable.test2)
        val imageOffset = 20.dp
        Card(
            shape = RoundedCornerShape(0.dp),
            backgroundColor = Color.DarkGray,
            modifier = Modifier
                .padding(bottom = imageOffset)
                .width(200.dp)
        ) {
            Column(
                horizontalAlignment = Alignment.CenterHorizontally,
                modifier = Modifier.padding(20.dp)
            ) {
                Text(
                    "Title",
                    modifier = Modifier.align(Alignment.Start)
                )
                Spacer(modifier = Modifier.size(15.dp))
                Text("PM2.5")
                Text(
                    "10",
                    fontSize = 35.sp,
                )
                Text("m/s")
                Spacer(modifier = Modifier.size(15.dp))
                Text(
                    "Very Good",
                    fontSize = 25.sp,
                )
                Spacer(
                    modifier = Modifier
                        .size(
                            with(LocalDensity.current) { imagePainter.intrinsicSize.height.toDp() - imageOffset }
                        )
                )
            }
        }
        Image(
            painter = imagePainter,
            contentDescription = "",
            Modifier
                .align(Alignment.BottomCenter)
        )
    }
}

Result:

Upvotes: 1

Gabriele Mariotti
Gabriele Mariotti

Reputation: 365028

A simple way is to use a Box and apply an Offset to the Image

Box() {
    val overlayBoxHeight = 40.dp
    Card(
        modifier = Modifier
            .fillMaxWidth()
            .height(300.dp)
    ) {
       //...
    }
    Image(
        painterResource(id = R.drawable.xxxx),
        contentDescription = "",
        modifier = Modifier
            .align(BottomCenter)
            .offset(x = 0.dp, y =  overlayBoxHeight )
    )
}

enter image description here

If you want to calculate the offset you can use a Layout composable.

Something like:

Layout( content = {
    Card(
        elevation = 10.dp,
        modifier = Modifier
            .layoutId("card")
            .fillMaxWidth()
            .height(300.dp)
    ) {
        //...
    }
    Image(
        painterResource(id = R.drawable.conero),
        contentDescription = "",
        modifier = Modifier
            .layoutId("image")
    )

}){ measurables, incomingConstraints ->

    val constraints = incomingConstraints.copy(minWidth = 0, minHeight = 0)
    val cardPlaceable =
        measurables.find { it.layoutId == "card" }?.measure(constraints)
    val imagePlaceable =
        measurables.find { it.layoutId == "image" }?.measure(constraints)

    //align the icon on the top/end edge
    layout(width = widthOrZero(cardPlaceable),
        height = heightOrZero(cardPlaceable)+ heightOrZero(imagePlaceable)/2){

        cardPlaceable?.placeRelative(0, 0)
        imagePlaceable?.placeRelative(widthOrZero(cardPlaceable)/2 - widthOrZero(imagePlaceable)/2,
            heightOrZero(cardPlaceable) - heightOrZero(imagePlaceable)/2)

    }
}

Upvotes: 4

Related Questions