Jetpack Compose には LazyVerticalGrid
というグリッド表示をしてくれる Composable がありますが、LazyColumn
など他のスクロール可能な Composable にネストしてグリッド表示を作りたい場合は LazyVerticalGrid
は利用できません(スクロール可能な Composable をネストしてしまうので)。
Android View にある GridLayout の代替となる Composable があればよいのですが、現状はまだないようです *1。
そこでなんとか GridLayout をうまく再現する方法として、accompanist にある FlowLayout を利用して実装してみます。 今回は Grid の各セルの横幅を画面幅やセル間のマージンから算出して設定し、GridLayout を横幅いっぱいに表示するものを作ります。あんまり汎用性はないです。
@Composable fun GridLayout( modifier: Modifier = Modifier items: List<String>, ) { // カラム数 val numOfColumns = 2 // セル間のマージン val gridSpacing = 8.dp, // セル間のマージンを取るための Spacer の数 val spacerCount = numOfColumns - 1 val config = LocalConfiguration.current val gridPadding = // ... グリッド両脇につける padding の dp // 画面の横幅からセル間のマージンとグリッド両脇の padding を取り除き、カラム数で割ってセル1個の横幅を出す val cellWidth = (config.screenWidthDp.dp - (gridSpacing * spacerCount) - (gridPadding * 2)) / numOfColumns Box( modifier = modifier.fillMaxWidth().wrapContentHeight() ) { FlowRow( modifier = Modifier.fillMaxWidth().wrapContentHeight() ) { // [0, 1, 2, 3, 4] のようなリストを [0, 1], [2, 3], [4] といった形の 2 個要素をもつチャンクに分割し、チャンクを Grid の 1 行分として扱う categoryTags.chunked(numOfColumns).forEach { chunkedList -> val left = chunkedList[0] // チャンクの最初の要素は必ず存在する val right = chunkedList.getOrNull(1) // チャンクの 2 個目の要素は存在しないかもしれない Box( modifier = Modifier .requiredWidth(cellWidth) .wrapContentHeight(), ) { // left を使って UI をつくる } // セル間のマージンを取る Spacer Spacer(modifier = Modifier.width(gridSpacing)) if (right == null) { // 要素はなくてもスペースは確保したい Spacer( modifier = Modifier .requiredWidth(cellWidth) .wrapContentHeight() ) } else { Box( modifier = Modifier .requiredWidth(cellWidth) .wrapContentHeight(), ) { // right を使って UI をつくる } } // 最後に、セルの下部にもマージンをつける。横幅いっぱいに Spacer を置いて、次のセルが確実にこの Spacer の下に来るようにする。 Spacer( modifier = Modifier .fillMaxWidth() .height(gridSpacing) ) } } } }
*1:Issue Tracker: https://issuetracker.google.com/issues/190893487 イシューは切られていますが優先度は高くないようです