Compose 流式布局


@Composable
fun StaggeredFlow(
    modifier: Modifier = Modifier,
    itemSpacing: Dp = 0.dp,
    lineSpacing: Dp = 0.dp,
    gravity: Int = Gravity.LEFT,
    content: @Composable () -> Unit
) {
    Layout(
        modifier = modifier,
        content = content
    ) { measurables, constraints ->
        var lineWidth = 0
        var totalHeight  = 0
        var lineHeight = 0
        var mAllPlaceables = mutableListOf>()
        // 每一行最高的高度
        var mLineHeight = mutableListOf()
        // 每行放置的内容
        var lineViews = mutableListOf()
        // 不要进一步限制子视图,使用给定的约束来测量它们
        val placeables = measurables.mapIndexed { i, measurable ->
            // 测量每个子视图
            val placeable = measurable.measure(constraints)
            var childWidth = placeable.width
            var childHeight = placeable.height
            if(lineWidth + childWidth > constraints.maxWidth){
                mLineHeight.add(lineHeight)
                mAllPlaceables.add(lineViews)
                lineViews = mutableListOf()
                lineViews.add(placeable)

                totalHeight +=lineHeight
                lineWidth = childWidth
                lineHeight = childHeight
                totalHeight += lineSpacing.toPx().toInt()
            }else{
                lineWidth += childWidth + if (i==0) 0 else itemSpacing.toPx().toInt()
                lineHeight = maxOf(lineHeight,childHeight)
                lineViews.add(placeable)
            }
            if(i == measurables.size - 1){
                totalHeight += lineHeight
                mLineHeight.add(lineHeight)
                mAllPlaceables.add(lineViews)
            }
        }
        // 设置父布局的大小,摆放子视图
        layout(constraints.maxWidth, totalHeight) {
            var topOffset = 0
            var leftOffset = 0
            for(i in mAllPlaceables.indices){
                lineViews = mAllPlaceables[i]
                lineHeight = mLineHeight[i]
                for (j in lineViews.indices){
                    val child = lineViews[j]
                    val childWidth = child.width
                    val childHeight = child.height
                    val childTop = getItemTop(gravity,lineHeight,topOffset,childHeight)
                    child.placeRelative(leftOffset,childTop)
                    leftOffset += childWidth + lineSpacing.toPx().toInt()
                }
                leftOffset = 0
                topOffset += lineHeight + lineSpacing.toPx().toInt()
            }
        }
    }
}

private fun getItemTop(gravity: Int,lineHeight: Int,topOffset: Int,childHeight: Int): Int {
    return when(gravity){
        Gravity.CENTER -> topOffset + (lineHeight-childHeight) / 2
        Gravity.BOTTOM -> topOffset + lineHeight-childHeight
        else -> topOffset
    }
}