我有一个屏幕,需要在其中显示标题和包含在卡片视图中的项目列表。整个屏幕必须可滚动(如下图所示)。
我知道如何使用可滚动列来做到这一点,但我希望能够使用
LazyColumn
(因为由于视图的复杂性,每个列表项都有自己的 ViewModel,我认为 LazyColumn 会更节省资源) )。对于标题,我可以使用 item
,对于列表,我可以使用 items
。下面是我尝试过的代码:
@Composable
fun Screen(
items: List<String>
) {
Column(
Modifier.fillMaxSize()
) {
TopAppBar(title = { Text(text = "My Activity") })
LazyColumn {
// Header
item {
Text("Title", Modifier.padding(32.dp))
}
// I cannot use Box in this way here
Box(Modifier.padding(32.dp)) {
Card {
items(items.size) {
Text("Item $it")
}
}
}
}
}
}
该代码的问题是我无法将列表项包装在卡片视图中,因为
Card
不是 LazyListScope
。使用 LazyColumn,如何将列表项包装在卡片中?
作为解决方法,您可以通过绘制自定义形状来模拟 Card。
sealed class Item
class HeaderItem(...): Item()
class ContentItem(...) : Item()
...
val items: SnapshotStateList<Item> = mutableStateListOf()
...
LazyColumn(
modifier = Modifier.fillMaxSize(),
) {
itemsIndexed(
items = state.items,
) { index, item ->
val prevItem = state.items.getOrNull(index - 1)
val nextItem = state.items.getOrNull(index + 1)
Column {
when (item) {
is HeaderItem -> Header(item)
is ContentItem -> {
Box(
modifier = Modifier
.heightIn(min = 48.dp)
.fillMaxWidth()
.clip(shape = getShape(prevItem, nextItem, 16.dp))
.background(Color.Green.copy(0.3F))
) {
Item(item)
}
}
}
}
}
fun getShape(prevItem: Item?, nextItem: Item?, corner: Dp): Shape {
return if (prevItem is ContentItem && nextItem is ContentItem) {
//FLAT
RectangleShape
} else if (prevItem !is ContentItem && nextItem !is ContentItem) {
//ROUNDED_ALL
RoundedCornerShape(corner)
} else if (prevItem !is ContentItem) {
//ROUNDED_TOP
RoundedCornerShape(topStart = corner, topEnd = corner)
} else {
//ROUNDED_BOTTOM
RoundedCornerShape(bottomStart = corner, bottomEnd = corner)
}
}
我正在寻找相同的内容,但不幸的是找不到解决方案,因此按照 @JanBína 在另一个类似问题下的建议我已经自定义了每个项目卡并获得了所需的输出,代码如下所示
@Composable
fun TransactionItem(
transaction: Transaction,
index: Int = 0,
totalSize: Int = 1,
onTransactionClicked: (Transaction) -> Unit
) {
val transactionDateTime = Instant.fromEpochMilliseconds(transaction.date).toLocalDateTime(
TimeZone.currentSystemDefault()
)
val cardMod = Modifier
.padding(horizontal = 5.dp)
.padding(bottom = if (index == totalSize - 1) 5.dp else 0.dp)
.fillMaxWidth()
.padding(horizontal = 5.dp)
if (totalSize == 1) {
cardMod.padding(vertical = 5.dp)
} else when (index) {
0 -> cardMod.padding(top = 5.dp)
totalSize - 1 -> cardMod.padding(bottom = 10.dp)
else -> cardMod.padding(vertical = 0.dp)
}
ElevatedCard(
onClick = {
onTransactionClicked(transaction)
},
modifier = cardMod,
shape = when (totalSize) {
1 -> RoundedCornerShape(15.dp)
else -> when (index) {
0 -> RoundedCornerShape(topStart = 15.dp, topEnd = 15.dp)
totalSize - 1 -> RoundedCornerShape(bottomStart = 15.dp, bottomEnd = 15.dp)
else -> RoundedCornerShape(0)
}
}
) {
///Item Content goes here
}
}
这不是最终的解决方案,我仍在寻找一个可以使用单张卡实现相同效果的解决方案,最终结果如下
你有点混淆了,你应该把
Card
放在 items
调用中:
LazyColumn {
// Header
item {
Text("Title", Modifier.padding(32.dp))
}
items(items.size) {
// You can use Box in this way here
Box(Modifier.padding(32.dp)) {
Card {
Text("Item $it")
}
}
}
}