Search code examples
androidkotlinlayoutandroid-jetpack-compose

Why columns are not all the same height?


I'm using Kotlin with Jetpack Compose for an Android app and I'm facing a problem that I'm struggling to solve. This is the result when I run the code below with COLUMNS equals to 7:

This is the result with COLUMNS equals to 8:

As you can see some times the columns are not all the same height (you can see the black background). How to solve this problem?

I tested the code below on 3 different devices and these are some the results:

With COLUMNS = 7:

  • with Google Pixel 2: the first column is shorter than the others (first image)
  • with Google Pixel C: the last 4 columns are shorter than the others
  • with Xiaomi Mi A2 Lite: the first column is shorter than the others

With COLUMNS = 8

  • with Google Pixel 2: the first 4 columns are shorter than the others
  • with Google Pixel C: the first 4 columns are shorter than the others
  • with Xiaomi Mi A2 Lite: the first 4 columns are shorter than the others

I simplified the code as much as possible to find the problem but still I can't find it:

package com.example.test

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.example.test.ui.theme.TestTheme
import kotlin.random.Random

const val COLUMNS = 11

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            TestTheme {
                Box(
                    modifier = Modifier.fillMaxSize(),
                    contentAlignment = Alignment.Center
                ) {
                    Table(modifier = Modifier.fillMaxWidth(.9f))
                }
            }
        }
    }
}

@Composable
fun Table(modifier: Modifier = Modifier) {
    Row(modifier = modifier.background(Color.Black)) {
        for (columnIndex in 0 until COLUMNS) {
            Column(
                modifier = Modifier
                    .background(Color(Random.nextInt(256), Random.nextInt(256), Random.nextInt(256)))
                    .weight(1f)
            ) {
                for (rowIndex in 0 until 5) {
                    Box(modifier = Modifier.aspectRatio(1f)) {
                        Box(modifier = Modifier.fillMaxSize()) {
                            // TODO
                        }
                    }
                }
            }
        }
    }
}

Solution

  • Because of rounding. You want to split some horizontal space W evenly between N columns. And when W mod N != 0, some columns will be 1 pixel narrower than the rest. The number of narrower columns depends on the device width and number of columns, as you realized.

    You then put 5 boxes with aspectRatio(1f) in each column, meaning the height of the column is 5 times its width, so the height difference is 5 pixels.