touchlane / gridpad-android

GridPad is a Jetpack Compose library that allows you to place UI elements in a predefined grid, manage spans in two dimensions, have flexible controls to manage row and column sizes.
MIT License
63 stars 5 forks source link

The row below not moved #26

Closed Deorigami closed 6 months ago

Deorigami commented 6 months ago
GridPad(
        cells = GridPadCells.Builder(rowCount = 6, columnCount = 5)
            .build()
    ) {
        repeat(10) {
            item(
                rowSpan = when (it) {
                    4 -> 2
                    else -> 1
                }
            ) {
                val isActive = false
                val binTint = if (isActive) primaryColor else Color(0xFFF2F8FB)
                val binTextColor = if (isActive) Color.White else Color(0xFFB5BCCB)
                Box(
                    modifier = Modifier.fillMaxSize().border(1.dp, Color.LightGray),
                    contentAlignment = Alignment.Center
                ) {
                    Image(
                        painterResource(Res.images.ic_shelf),
                        "",
                        colorFilter = ColorFilter.tint(binTint)
                    )
                    Text(
                        "1",
                        style = style500_10.copy(binTextColor),
                        modifier = Modifier.offset(y = 2.dp)
                    )
                }
            }
        }
    }

image

why is the grid move like this .. and not like your calculator ?

landarskiy commented 6 months ago

Hi @Deorigami , and thanks for your interest in this library.

The behaviour you encountered is related to the implicit placement order of elements. It's possible that your expectations didn't align with the result because the implicit algorithm can be a bit tricky, especially when dealing with different spans.

In your case, the implicit algorithm identifies the last row as 1 (row index) because there is a spanned item present. The algorithm then increments it by 1 and starts the placement from the beginning in the next line (row index 2). In the sample app, all these cases are handled by explicit placement of elements, and there are at least 2 major reasons for this:

  1. There might be plenty of various scenarios with different combinations of span sizes, making it almost impossible to predict and achieve a "nice-looking" placement.
  2. The library allows elements to cover, meaning that if, in your case, the library continued to place elements from the second row (index 1), the last item would be placed on the spanned button (row index = 1, column index = 4).

I recommend following the recommendation to avoid excessive use of implicit placement, especially in cases involving spanned elements. Making these adjustments will lead to more predictable and desired outcomes.

To summarise, you solution MIGHT look like (I changed some resources a bit to run it locally):

@Composable
fun GridWorkaround(modifier: Modifier = Modifier) {
    GridPad(
        modifier = modifier,
        cells = GridPadCells.Builder(rowCount = 6, columnCount = 5).build()
    ) {
        repeat(4) {
            item {
                BoxItem(isActive = false)
            }
        }
        item(rowSpan = 2) {
            BoxItem(isActive = false)
        }
        // here we set expected start point to continue implicit placement
        item(row = 1, column = 0) {
            BoxItem(isActive = false)
        }
        repeat(3) {
            item {
                BoxItem(isActive = false)
            }
        }
        // to avoid placement on already placed spanned item
        item(row = 2, column = 0) {
            BoxItem(isActive = false)
        }
    }
}

@Composable
fun BoxItem(isActive: Boolean, modifier: Modifier = Modifier) {
    val binTint = if (isActive) Color(0xFFB5BCCB) else Color(0xFFF2F8FB)
    val binTextColor = if (isActive) Color(0xFFFFFFFF) else Color(0xFFB5BCCB)
    Box(
        modifier = modifier
            .fillMaxSize()
            .border(1.dp, Color.LightGray),
        contentAlignment = Alignment.Center
    ) {
        Image(
            painterResource(R.drawable.ic_launcher_foreground),
            "",
            colorFilter = ColorFilter.tint(binTint)
        )
        Text(
            "1",
            style = LocalTextStyle.current.copy(binTextColor),
            modifier = Modifier.offset(y = 2.dp)
        )
    }
}

And here is the result of code above:

Screenshot 2024-01-18 at 15 12 35
Deorigami commented 6 months ago

in my case .. i need to make it dynamic from the server .. so i guess its need a lot of works then :D

landarskiy commented 6 months ago

I don't think so. You can calculate all places (row and col index) in the server, receive the data in the client and place all items explicitly. And you can implement any logic for placement you want!