如何使用 Jetpack Compose 创建 GridView
How to create GridView using Jetpack Compose
如何在不使用回收视图或 android.widget.gridview 的情况下在 Jetpack Compose 中创建 Gridview?
通过 1.0.x
,LazyVerticalGrid
可组合项为在网格中显示项目提供实验性支持。
val numbers = (0..20).toList()
LazyVerticalGrid(
cells = GridCells.Fixed(4)
) {
items(numbers.size) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "Number")
Text(text = " $it",)
}
}
}
cells = GridCells.Fixed(4)
表示有 4 列占父级宽度的 1/4。
val numbers = (0..20).toList()
LazyVerticalGrid(
cells = GridCells.Adaptive(minSize = 64.dp)
) {
items(numbers) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "Number")
Text(text = " $it",)
}
}
}
cells = GridCells.Adaptive(minSize = 64.dp)
意味着会有尽可能多的列,每列至少 64.dp 并且所有列的宽度都相等。
我已经为使用 android jetpack compose 创建了自定义 gridview,直到它们不支持 Compose 中 Gridlayout 的官方回收视图。
@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
}
}
}
}
}
}
}
使用
GridView(cols = 4, list = model.list,child = { Item( it) })
物品声明
@Composable
fun Item(t: T) {
....
}
对@Madhav 的回答进行了一些更改(使用 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 }
}
}
}
}
}
用法:
GridView(cols = 2, list = listOf("1", "2", "3", "4",)) {
Text(text = it)
}
UPD:Compose版本1.0.0-alpha09引入标准组件:
LazyVerticalGrid
又一个基于 LazyColumnFor 的解决方案(Jetpack Compose 版本 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)
}
}
如@Pavel Marchenko 所述,LazyVerticalGrid
是从版本 1.0.0-alpha09
添加的
这是一个简单的例子:
LazyVerticalGrid(
cells = GridCells.Adaptive(96.dp),
contentPadding = PaddingValues(16.dp),
) {
items(bookList) { book ->
Image(book.cover, modifier = Modifier.padding(8.dp))
}
}
我创建了自适应网格布局:
预览
代码
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))
}
...
}
完整代码为here。
我决定实现我自己的自适应网格布局,因为现有的 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
递归:
@ExperimentalFoundationApi
@Composable
fun A {
LazyVerticalGrid {
...
}
}
@ExperimentalFoundationApi
@Composable
fun B {
A {..}
}
@ExperimentalFoundationApi
@Composable
fun C {
B {..}
}
...
或使用需要-Xopt-in=kotlin.RequiresOptIn
编译器参数的@OptIn(ExperimentalFoundationApi::class)
。
更新@Pavel Marchenko 的回答,因为某些撰写函数的名称已更改:
LazyColumn() 而不是 LazyColumnFor() 并且需要使用 items() 函数:
@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)
}
}
}
}
}
}
由于 LazyVerticalGrid 不能在 LazyColumn 中使用,因此不能在可滚动页面中使用它。我在 compose 中为自定义 LazyGrid 实现创建了一个库。它很懒惰,所以表现非常好。不同场景有不同的物品放置类型,真的好用:
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 */ })
}
}
还有一个可折叠的版本。更多信息、文档、源代码和演示可在以下位置找到:https://github.com/yusufarisoy/lazy-grid
dependencies {
implementation 'com.github.yusufarisoy:lazy-grid:1.0.0'
}
它也是可重启和可跳过的,以便 compose 编译器以最佳性能工作。关于 Compose 指标:https://chris.banes.dev/composable-metrics/
如何在不使用回收视图或 android.widget.gridview 的情况下在 Jetpack Compose 中创建 Gridview?
通过 1.0.x
,LazyVerticalGrid
可组合项为在网格中显示项目提供实验性支持。
val numbers = (0..20).toList()
LazyVerticalGrid(
cells = GridCells.Fixed(4)
) {
items(numbers.size) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "Number")
Text(text = " $it",)
}
}
}
cells = GridCells.Fixed(4)
表示有 4 列占父级宽度的 1/4。
val numbers = (0..20).toList()
LazyVerticalGrid(
cells = GridCells.Adaptive(minSize = 64.dp)
) {
items(numbers) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "Number")
Text(text = " $it",)
}
}
}
cells = GridCells.Adaptive(minSize = 64.dp)
意味着会有尽可能多的列,每列至少 64.dp 并且所有列的宽度都相等。
我已经为使用 android jetpack compose 创建了自定义 gridview,直到它们不支持 Compose 中 Gridlayout 的官方回收视图。
@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
}
}
}
}
}
}
}
使用
GridView(cols = 4, list = model.list,child = { Item( it) })
物品声明
@Composable
fun Item(t: T) {
....
}
对@Madhav 的回答进行了一些更改(使用 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 }
}
}
}
}
}
用法:
GridView(cols = 2, list = listOf("1", "2", "3", "4",)) {
Text(text = it)
}
UPD:Compose版本1.0.0-alpha09引入标准组件:
LazyVerticalGrid
又一个基于 LazyColumnFor 的解决方案(Jetpack Compose 版本 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)
}
}
如@Pavel Marchenko 所述,LazyVerticalGrid
是从版本 1.0.0-alpha09
这是一个简单的例子:
LazyVerticalGrid(
cells = GridCells.Adaptive(96.dp),
contentPadding = PaddingValues(16.dp),
) {
items(bookList) { book ->
Image(book.cover, modifier = Modifier.padding(8.dp))
}
}
我创建了自适应网格布局:
预览
代码
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))
}
...
}
完整代码为here。
我决定实现我自己的自适应网格布局,因为现有的 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
递归:
@ExperimentalFoundationApi
@Composable
fun A {
LazyVerticalGrid {
...
}
}
@ExperimentalFoundationApi
@Composable
fun B {
A {..}
}
@ExperimentalFoundationApi
@Composable
fun C {
B {..}
}
...
或使用需要-Xopt-in=kotlin.RequiresOptIn
编译器参数的@OptIn(ExperimentalFoundationApi::class)
。
更新@Pavel Marchenko 的回答,因为某些撰写函数的名称已更改: LazyColumn() 而不是 LazyColumnFor() 并且需要使用 items() 函数:
@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)
}
}
}
}
}
}
由于 LazyVerticalGrid 不能在 LazyColumn 中使用,因此不能在可滚动页面中使用它。我在 compose 中为自定义 LazyGrid 实现创建了一个库。它很懒惰,所以表现非常好。不同场景有不同的物品放置类型,真的好用:
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 */ })
}
}
还有一个可折叠的版本。更多信息、文档、源代码和演示可在以下位置找到:https://github.com/yusufarisoy/lazy-grid
dependencies {
implementation 'com.github.yusufarisoy:lazy-grid:1.0.0'
}
它也是可重启和可跳过的,以便 compose 编译器以最佳性能工作。关于 Compose 指标:https://chris.banes.dev/composable-metrics/