How to create Gridview in Jetpack compose without using recycler view or android.widget.gridview ?
With 1.x.y
the LazyVerticalGrid
composable provides experimental support for displaying items in a grid.
val numbers = (0..20).toList()
LazyVerticalGrid(
columns = GridCells.Fixed(4)
) {
items(numbers.size) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "Number")
Text(text = " $it",)
}
}
}
The columns = GridCells.Fixed(4)
would mean that there are 4 columns 1/4 of the parent wide.
val numbers = (0..20).toList()
LazyVerticalGrid(
columns = GridCells.Adaptive(minSize = 64.dp)
) {
items(numbers) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "Number")
Text(text = " $it",)
}
}
}
columns = GridCells.Adaptive(minSize = 64.dp)
would mean that there will be as many columns as possible and every column will be at least 64.dp and all the columns will have equal width.
UPD: Compose version 1.0.0-alpha09 introduces standard component:
LazyVerticalGrid
Yet another solution based on LazyColumnFor (Jetpack Compose version 1.0.0-alpha04)
@Composable
fun <T> LazyGridFor(
items: List<T>,
rowSize: Int = 1,
itemContent: @Composable BoxScope.(T) -> Unit,
) {
val rows = items.chunked(rowSize)
LazyColumnFor(rows) { row ->
Row(Modifier.fillParentMaxWidth()) {
for ((index, item) in row.withIndex()) {
Box(Modifier.fillMaxWidth(1f / (rowSize - index))) {
itemContent(item)
}
}
}
}
}
@Preview("LazyGridFor: example")
@Composable()
fun LazyGridForPreview() {
val data = (1..100).map(Integer::toString)
LazyGridFor(data, 3) { item ->
Text(item)
}
}
LazyVerticalGrid
with paging data... –
Blurt LazyColumnFor
doesn't exist no more –
Blurt I have created a adaptative grid layout:
Preveiw
Code
LazyColumn(modifier = modifier) {
...
val numberOfItemsByRow = LocalConfiguration.current.screenWidthDp / 200 // you can replace 200 by the minimum size you want your cells to have.
items(items = trendingGameList.chunked(numberOfItemsByRow)) { rowItems ->
Row(
horizontalArrangement = Arrangement.spacedBy(14.dp),
modifier = Modifier.padding(horizontal = 16.dp),
) {
for (game in rowItems) {
GameCard(game = game, onClick = { }, modifier = Modifier.weight(1F))
}
}
Spacer(Modifier.height(14.dp))
}
...
}
The full code is here.
I decided to implement my own adaptative grid layout because the existing LazyVerticalGrid is experimental and can be removed in the future, and to use it you have to annotate the compostables that uses it with @ExperimentalFoundationApi
recursively:
@ExperimentalFoundationApi
@Composable
fun A {
LazyVerticalGrid {
...
}
}
@ExperimentalFoundationApi
@Composable
fun B {
A {..}
}
@ExperimentalFoundationApi
@Composable
fun C {
B {..}
}
...
OR use the @OptIn(ExperimentalFoundationApi::class)
which require the -Xopt-in=kotlin.RequiresOptIn
compiler argument.
As @Pavel Marchenko mentioned, LazyVerticalGrid
is added from version 1.0.0-alpha09
Here is a quick example:
LazyVerticalGrid(
cells = GridCells.Adaptive(96.dp),
contentPadding = PaddingValues(16.dp),
) {
items(bookList) { book ->
Image(book.cover, modifier = Modifier.padding(8.dp))
}
}
Update to @Pavel Marchenko answer as some of the compose function's names changed: LazyColumn() instead of LazyColumnFor() and use of items() functions is needed:
@Composable
fun <T> LazyGridFor(
items: List<T>,
rowSize: Int = 1,
itemContent: @Composable BoxScope.(T) -> Unit,
) {
LazyColumn {
items(items = items.chunked(rowSize)) { row ->
Row(Modifier.fillParentMaxWidth()) {
for ((index, item) in row.withIndex()) {
Box(Modifier.fillMaxWidth(1f / (rowSize - index))) {
itemContent(item)
}
}
}
}
}
}
I have created a custom gridview for using android jetpack compose until they will not support official recycleview for Gridlayout in Compose.
@Composable
fun <T> GridView(
cols: Int = 0,
list: List<T>,
child: @Composable() (dataModal: T) -> Unit
) {
val rows = (list.size / cols) + (if (list.size % cols > 0) 1 else 0)
VerticalScroller(modifier =
Modifier.fillMaxHeight().fillMaxHeight().drawBackground(color = colorResource(
id = R.color.color_bg_application
))) {
Table(columns = cols) {
for (r in 0 until rows) {
tableRow {
for (c in 0 until cols) {
//Cell
val i = (r * cols) + c
if (i < list.size) {
child(list[i])
} else {
break
}
}
}
}
}
}
}
USE
GridView(cols = 4, list = model.list,child = { Item( it) })
Item declaration
@Composable
fun Item(t: T) {
....
}
Since LazyVerticalGrid can't be used in a LazyColumn, can't use it in a scrollable page. I created a library for custom LazyGrid implementation in compose. It's lazy so it performs really well. There are different item placement types for different scenarios and it's really easy to use:
LazyColumn {
item {
Text(text = "Title")
}
LazyGrid(
rows = listOf(),
elementPerRow = 4,
itemPlacementType = ItemPlacementType.FixedSize(itemWidth = 80.dp),
contentPadding = PaddingValues(horizontal = 16.dp)
) { item, modifier ->
CustomGridItem(item, modifier, onClick = { /* if needed */ })
}
}
There is also a collapsible version. Further information, documentation, source code and a demo can be found in: https://github.com/yusufarisoy/lazy-grid
dependencies {
implementation 'com.github.yusufarisoy:lazy-grid:1.0.0'
}
It's also restartable and skippable for compose compiler to work with best performance. About Compose metrics: https://chris.banes.dev/composable-metrics/
LazyVerticalGrid Consumes the scroll state thereby making it useless inside a LazyColum
Here an implementation using only rows and columns
sealed class GridCells {
abstract val columns: Int
data class Fixed(val count: Int) : GridCells() {
override val columns: Int = count
}
object Adaptive : GridCells() {
override val columns: Int = 1
}}
@Composable
fun VerticalGrid(
modifier: Modifier = Modifier,
gridCells: GridCells,
totalItems: Int,
contentPadding: PaddingValues = PaddingValues(0.dp),
content: @Composable (Int) -> Unit,
) {
Column(modifier = modifier) {
val columnCount = when (gridCells) {
is GridCells.Fixed -> gridCells.columns
is GridCells.Adaptive -> ceil(sqrt(totalItems.toDouble())).toInt()
}
val rowCount = ceil(totalItems.toDouble()/columnCount).toInt()
for(i in 0 until rowCount){
Row(Modifier.fillMaxWidth()) {
for (j in 0 until columnCount) {
val index = j + i * columnCount
Box(
Modifier
.weight(1f)
.padding(contentPadding)
) {
if (index < totalItems) {
content(index)
}
}
}
}
}
}
}
Made a couple of changes to @Madhav 's answer (using compose v1.0.0-alpha01
):
@Composable
fun <T> GridView(
cols: Int = 1,
list: List<T>,
rowModifier: Modifier = Modifier,
colModifier: Modifier = Modifier,
child: @Composable (dataModal: T) -> Unit
) {
val rows = (list.size / cols) + (if (list.size % cols > 0) 1 else 0)
ScrollableColumn(modifier = colModifier) {
for (r in 0 until rows) {
Row(modifier = rowModifier, horizontalArrangement = Arrangement.SpaceAround) {
for (cell in 0 until cols) {
val i = (r * cols) + cell
if (i < list.size) { child(list[i]) } else { break }
}
}
}
}
}
Usage:
GridView(cols = 2, list = listOf("1", "2", "3", "4",)) {
Text(text = it)
}
I can't add a LazyVerticalGrid inside a LazyColumn so found this way. Let's check it.
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun CustomGridView(
cols: Int,
count: Int,
builder: @Composable (Int) -> Unit
) {
FlowRow(
maxItemsInEachRow = cols,
) {
repeat(count) {
Box(Modifier.fillMaxWidth((1.0 / cols).toFloat())) {
builder(it)
}
}
}
}
Example:
CustomGridView (
cols = 2,
count = data.count(),
) {
index ->
DetailsPieChartItem(
data = Pair(data.keys.elementAt(index), data.values.elementAt(index)),
color = colors[index]
)
}
Preview:
© 2022 - 2024 — McMap. All rights reserved.