我想创建表格视图,就像下面这个例子一样,来展示我拥有的数据。
一个标题 | 另一个标题 |
---|---|
第一行 | 数据 |
第二行 | 数据 |
我尝试使用LazyVerticalGrid
来实现,但是Jetpack Compose不允许我将LazyVerticalGrid
放在一个垂直滚动的Column
中。
已经过去两天了,我真的没有任何想法。
我想创建表格视图,就像下面这个例子一样,来展示我拥有的数据。
一个标题 | 另一个标题 |
---|---|
第一行 | 数据 |
第二行 | 数据 |
我尝试使用LazyVerticalGrid
来实现,但是Jetpack Compose不允许我将LazyVerticalGrid
放在一个垂直滚动的Column
中。
已经过去两天了,我真的没有任何想法。
LazyColumn
并将同一列的所有行使用相同的weight
实际上很容易实现。首先,您可以为您的表格定义一个单元格:
@Composable
fun RowScope.TableCell(
text: String,
weight: Float
) {
Text(
text = text,
Modifier
.border(1.dp, Color.Black)
.weight(weight)
.padding(8.dp)
)
}
然后您可以使用它来构建您的表格:
@Composable
fun TableScreen() {
// Just a fake data... a Pair of Int and String
val tableData = (1..100).mapIndexed { index, item ->
index to "Item $index"
}
// Each cell of a column must have the same weight.
val column1Weight = .3f // 30%
val column2Weight = .7f // 70%
// The LazyColumn will be our table. Notice the use of the weights below
LazyColumn(Modifier.fillMaxSize().padding(16.dp)) {
// Here is the header
item {
Row(Modifier.background(Color.Gray)) {
TableCell(text = "Column 1", weight = column1Weight)
TableCell(text = "Column 2", weight = column2Weight)
}
}
// Here are all the lines of your table.
items(tableData) {
val (id, text) = it
Row(Modifier.fillMaxWidth()) {
TableCell(text = id.toString(), weight = column1Weight)
TableCell(text = text, weight = column2Weight)
}
}
}
}
这是结果:
LazyColumn
有关的问题。在其中无法填充Android视图。 - Kevinandroidx.compose.material:material:1.0.3
似乎不支持在Text()
中使用Modifier.weight()
修饰符?这会导致编译失败。 - Sean Barbeau同时支持固定和可变列宽,并且可以水平和垂直滚动的实现方式如下:
@Composable
fun Table(
modifier: Modifier = Modifier,
rowModifier: Modifier = Modifier,
verticalLazyListState: LazyListState = rememberLazyListState(),
horizontalScrollState: ScrollState = rememberScrollState(),
columnCount: Int,
rowCount: Int,
beforeRow: (@Composable (rowIndex: Int) -> Unit)? = null,
afterRow: (@Composable (rowIndex: Int) -> Unit)? = null,
cellContent: @Composable (columnIndex: Int, rowIndex: Int) -> Unit
) {
val columnWidths = remember { mutableStateMapOf<Int, Int>() }
Box(modifier = modifier.then(Modifier.horizontalScroll(horizontalScrollState))) {
LazyColumn(state = verticalLazyListState) {
items(rowCount) { rowIndex ->
Column {
beforeRow?.invoke(rowIndex)
Row(modifier = rowModifier) {
(0 until columnCount).forEach { columnIndex ->
Box(modifier = Modifier.layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
val existingWidth = columnWidths[columnIndex] ?: 0
val maxWidth = maxOf(existingWidth, placeable.width)
if (maxWidth > existingWidth) {
columnWidths[columnIndex] = maxWidth
}
layout(width = maxWidth, height = placeable.height) {
placeable.placeRelative(0, 0)
}
}) {
cellContent(columnIndex, rowIndex)
}
}
}
afterRow?.invoke(rowIndex)
}
}
}
}
}
这种实现的好处是提供了更大的灵活性和相对简单的API。然而,已知有一些注意事项,包括:可变宽度列的性能较差,并且列分隔符必须在单元格级别上完成。
对于可变宽度列,它不像固定宽度列那样性能好,因为它需要多次“布局遍历”。基本上,此实现在LazyColumn
中布置每个Row
,测量每行中的每个列单元格并将最大宽度值存储在MutableState
中。当遇到更大的宽度值时,它会触发重新组合,调整该列中的所有其他单元格以使其成为更大的宽度。这样,列中的每个单元格具有相同的宽度(行中的每个单元格具有相同的高度)。对于固定宽度列,性能应与其他实现相当,因为它不需要多次“布局遍历”。
使用方法:
Table(
modifier = Modifier.matchParentSize(),
columnCount = 3,
rowCount = 10,
cellContent = { columnIndex, rowIndex ->
Text("Column: $columnIndex; Row: $rowIndex")
})
使用上述实现创建过载的可组合函数应该相当简单,比如将“标题行”作为表中的第一行。
我的解决方案并不完美,但至少水平滚动是有效的。最好的解决方案我没有找到也没有想出来。希望Google发布自己的DataTable实现。目前Android还没有实现DataTable。 要设置列的宽度,必须计算它们的权重,但这种计算并不精确。
private fun calcWeights(columns: List<String>, rows: List<List<String>>): List<Float> {
val weights = MutableList(columns.size) { 0 }
val fullList = rows.toMutableList()
fullList.add(columns)
fullList.forEach { list ->
list.forEachIndexed { columnIndex, value ->
weights[columnIndex] = weights[columnIndex].coerceAtLeast(value.length)
}
}
return weights
.map { it.toFloat() }
}
@Composable
fun SimpleTable(columnHeaders: List<String>, rows: List<List<String>>) {
val weights = remember { mutableStateOf(calcWeights(columnHeaders, rows)) }
Column(
modifier = Modifier
.horizontalScroll(rememberScrollState())
) {
/* HEADER */
Row(modifier = Modifier.fillMaxWidth()) {
columnHeaders.forEachIndexed { rowIndex, cell ->
val weight = weights.value[rowIndex]
SimpleCell(text = cell, weight = weight)
}
}
/* ROWS */
LazyColumn(modifier = Modifier) {
itemsIndexed(rows) { rowIndex, row ->
Row(
modifier = Modifier
.fillMaxWidth()
) {
row.forEachIndexed { columnIndex, cell ->
val weight = weights.value[columnIndex]
SimpleCell(text = cell, weight = weight)
}
}
}
}
}
}
@Composable
private fun SimpleCell(
text: String,
weight: Float = 1f
) {
val textStyle = MaterialTheme.typography.body1
val fontWidth = textStyle.fontSize.value / 2.2f // depends of font used(
val width = (fontWidth * weight).coerceAtMost(500f)
val textColor = MaterialTheme.colors.onBackground
Text(
text = text,
maxLines = 1,
softWrap = false,
overflow = TextOverflow.Ellipsis,
color = textColor,
modifier = Modifier
.border(0.dp, textColor.copy(alpha = 0.5f))
.fillMaxWidth()
.width(width.dp + Size.marginS.times(2))
.padding(horizontal = 4.dp, vertical = 2.dp)
)
}
DataTable
已不再可用... - Kevin