简介
- 在xml向Compose的迁移过程中遇到的问题,瀑布流没有官方的控件,但在官方的owl demo中看到了自定义的
- owl直接自定义操作的Layout,测量和分配位置,最外层要套一层滑动的Column
- 有些第三方作者写的是利用两个LazyColumn经过改造达到目的
- 现在体验的效果还是感觉官方的流畅一些,但也存在问题 列表大的时候可能会触发OOM ,下拉加载更多要自己想办法实现,不能结合paging来做
附上我学习Compose的代码(demo中有三个实现方案) github
创建数据
图片的数据需要VPN,来自谷歌的demo图
private val rangeForRandom = (0..100000)
data class StaggeredItem(
val name: String,
val height: Int,
val color: Color,
val picture: String,
)
fun getStaggeredList(): MutableList<StaggeredItem> {
val list = mutableListOf<StaggeredItem>()
val heightList = listOf(300, 320, 350, 280)
repeat(500) {
val height = heightList.random()
val picture = "https://picsum.photos/seed/${rangeForRandom.random()}/500/$height"
list.add(
StaggeredItem(
name = "name $it",
height = height,
color = Color(
red = (0..255).random(),
blue = (0..255).random(),
green = (0..255).random()
),
picture = picture
)
)
}
return list
}
使用
@Composable
fun ListOne() {
val list = getStaggeredList()
Column(
modifier = Modifier.verticalScroll(
rememberScrollState(),
flingBehavior = StockFlingBehaviours.smoothScroll()
)
) {
StaggeredVerticalGrid(
maxColumnWidth = 220.dp,
modifier = Modifier.padding(4.dp)
) {
list.forEachIndexed { _, staggeredItem ->
StaggeredItem(staggeredItem)
}
}
}
}
Item
@Composable
fun StaggeredItem(staggeredItem: StaggeredItem) {
Box(
modifier = Modifier
.padding(horizontal = 4.dp, vertical = 4.dp)
.background(color = staggeredItem.color, shape = RoundedCornerShape(9.dp))
.fillMaxWidth()
.height(staggeredItem.height.dp),
contentAlignment = Alignment.Center
) {
XLogger.d("---->${staggeredItem.picture}")
Image(
painter = rememberImagePainter(data = staggeredItem.picture),
contentDescription = null,
contentScale = ContentScale.FillBounds,
modifier = Modifier
.fillMaxSize()
.clip(RoundedCornerShape(9.dp))
)
Text(
text = staggeredItem.name,
color = Color.White,
fontSize = 25.sp,
fontWeight = FontWeight.Medium
)
}
}
StaggeredVerticalGrid
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.unit.Dp
import kotlin.math.ceil
@Composable
fun StaggeredVerticalGrid(
modifier: Modifier = Modifier,
maxColumnWidth: Dp,
content: @Composable () -> Unit
) {
Layout(
modifier = modifier,
content = content,
) { measurables, constraints ->
check(constraints.hasBoundedWidth) {
"Unbounded width not supported"
}
val columns = ceil(constraints.maxWidth / maxColumnWidth.toPx()).toInt()
val columnWidth = constraints.maxWidth / columns
val itemConstraints = constraints.copy(maxWidth = columnWidth)
val colHeights = IntArray(columns) { 0 } // track each column's height
val placeables = measurables.map { measurable ->
val column = shortestColumn(colHeights)
val placeable = measurable.measure(itemConstraints)
colHeights[column] += placeable.height
placeable
}
val height = colHeights.maxOrNull()?.coerceIn(constraints.minHeight, constraints.maxHeight)
?: constraints.minHeight
layout(
width = constraints.maxWidth,
height = height
) {
val colY = IntArray(columns) { 0 }
placeables.forEach { placeable ->
val column = shortestColumn(colY)
placeable.place(
x = columnWidth * column,
y = colY[column]
)
colY[column] += placeable.height
}
}
}
}
private fun shortestColumn(colHeights: IntArray): Int {
var minHeight = Int.MAX_VALUE
var column = 0
colHeights.forEachIndexed { index, height ->
if (height < minHeight) {
minHeight = height
column = index
}
}
return column
}