Psijic
Psijic

Reputation: 992

Get size of an element in Jetpack Compose

I'm making an isometric grid and using padding for it cells. Currently as parameters I'm waiting for its items width and height. But how can I avoid this and use final cells size in an optimal way?

I tried some solutions (commented code is the original way) from this question but it doesn't look legit for the case: I have extra multiple calculations, an error: Padding must be non-negative and it doesn't show correctly in Android Studio preview.

Modifier.onGloballyPositioned also looks same and incorrect for the case, what's the right way?

@Composable
fun <T> IsometricGrid(
    gridWidth: Int,
    gridHeight: Int,
    cellWidth: Int,
    cellHeight: Int,
    data: List<T>,
    itemContent: @Composable (Int, T) -> Unit
) {
    var width by remember { mutableStateOf(0) }
    var height by remember { mutableStateOf(0) }

    for (y in 0 until gridHeight) {
        for (x in 0 until gridWidth) {
//            val start = (y % 2 * 0.5 + x) * cellWidth
//            val top = y * cellHeight * 0.5
            val index = x * gridHeight + y

            Box(
                modifier = Modifier
                    .onSizeChanged {
                        width = it.width
                        height = it.height
                        Timber.i("$width, $height")
                    }
//                    .padding(start = start.dp, top = top.dp)
                    .padding(start = ((y % 2 * 0.5 + x) * cellWidth).dp, top = (y * height * 0.5).dp)
            ) {
                itemContent(index, data[index])
            }
        }
    }
}

Added usage:

IsometricGrid(4, 4, 100, 50, listOf<Foo>(...)) { index: Int, foo: Foo ->
    Icon(...)
}

Upvotes: 0

Views: 1524

Answers (1)

Thracian
Thracian

Reputation: 66674

start = (y % 2 * 0.5 + x * width).dp, top = (y * height * 0.5).dp

This line is not correct because you add dp extension to pixel instead of converting pixel to dp

What you should be doing

val density = LocalDensity.current
density.run{(y % 2 * 0.5 + x * width).toDp()}

because dp value of any pixel values is calculated as

dpValue = valueInPixel/density

let's say you have 100px with a density = 2.0f

your dp value should be 50.dp. If you calculate it as in your question you find that it returns 100.dp

Try this. I don't have data, so i'm not able to try your function.

@Composable
fun <T> IsometricGrid(
    gridWidth: Int,
    gridHeight: Int,
    cellWidth: Int,
    cellHeight: Int,
    data: List<T>,
    itemContent: @Composable (Int, T) -> Unit
) {

    val density = LocalDensity.current

    var width by remember { mutableStateOf(0) }
    var height by remember { mutableStateOf(0) }

    for (y in 0 until gridHeight) {
        for (x in 0 until gridWidth) {
//            val start = (y % 2 * 0.5 + x) * cellWidth
//            val top = y * cellHeight * 0.5
            val index = x * gridHeight + y

            val startInDp = density.run { ((y % 2 * 0.5f + x) * cellWidth).toDp() }
            val topInDp = density.run { (y * height * 0.5f).toDp() }

            Box(
                modifier = Modifier
                    .onSizeChanged {
                        width = it.width
                        height = it.height
                    }
//                    .padding(start = start.dp, top = top.dp)
                    .padding(
                        start = startInDp,
                        top = topInDp
                    )
            ) {
                itemContent(index, data[index])
            }
        }
    }
}

toDp() is extension for Float in Density interface

/** Convert a [Float] pixel value to a Dp */
@Stable
fun Float.toDp(): Dp = (this / density).dp

Upvotes: 2

Related Questions