Kotlin Compose 自定义布局 StaggeredGrid

要实现效果

 是可以往左边滑动的。

第一步我们写card 模块

就是里面的子布局

package com.anguomob.jecpack.activity.compose.layout

import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Card
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.anguomob.jetpack.ui.theme.ComposeTheme

@Composable
fun StaggeredGrid(modifier: Modifier = Modifier) {
    ComposeTheme {
        Chip(modifier =modifier , text = "Arts % Crafts")
    }
}

@Composable
fun Chip(modifier: Modifier, text: String) {
    //一个卡片 圆角,里面包含一个Row,第一列是Box 第二例是文本
    Card(
        modifier = modifier,
        //Hairline 发际线 默认一个像素
        border = BorderStroke(color = Color.Black, width = Dp.Hairline),
        shape = RoundedCornerShape(8.dp)
    ) {
        Row(
            modifier = Modifier.padding(start = 8.dp, top = 4.dp, end = 8.dp, bottom = 8.dp),
            verticalAlignment = Alignment.CenterVertically
        ) {
            Box(
                modifier = Modifier
                    .size(16.dp)
                    .background(color = MaterialTheme.colors.secondary)

            )
            Spacer(modifier = Modifier.width(4.dp))
            Text(text)
        }
    }
}

效果

再进行分析这个大的布局

经过一系列的计算 父布局就是这样

扫描二维码关注公众号,回复: 14290744 查看本文章
@Composable
fun StaggeredGrid(modifier: Modifier = Modifier, rows: Int = 3, content: @Composable () -> Unit) {
    Layout(modifier = modifier, content = content) { measurables, constraints ->
        //用于保存每行的宽度数值
        val rowWidths = IntArray(rows) { 0 }
        //用于保存每行的高度数值
        val rowHeightss = IntArray(rows) { 0 }

        val placeables = measurables.mapIndexed { index, measurable ->
            //测量每一个元素
            val placeable = measurable.measure(constraints)
            //计算每一行的宽度与高度
            //元素下标。假设总共11个元素
            //index 0 1 2 3 4 5 6 7 8 9 10
            //行数 假设3行
            //rows=3
            //保存行高数组的下标值:
            //row:0,1,2
            val row = index % rows
            //一行的宽度等于这一行所有元素宽度之和
            rowWidths[row] += placeable.width
            //一行的高度应该是最高的那个元素的高度
            rowHeightss[row] = kotlin.math.max(rowHeightss[row], placeable.height)
            placeable
        }
        //计算表格的高度
        //表格的宽度应该是所有行当中最宽的哪一行的宽度
        val width = rowWidths.maxOrNull() ?: constraints.minWidth
        //表格的高度应该是所有高度之和
        val height = rowHeightss.sumOf { it }
        //设置每一行的y坐标
        val rowY = IntArray(rows) { 0 }
        //索引从1开始,因为第一行y坐标为0,rows[0]=0
        for (i in 1 until rows) {
//            rowY[1] = rowY[0] + rowHeightss[0]
            rowY[i] = rowY[i - 1] + rowHeightss[i - 1]
        }

        layout(width, height) {
            val rowX = IntArray(rows) { 0 }
            placeables.forEachIndexed { index, placeable ->
                //index 0 1 2 3 4 5 6 7 8 9 10
                //行数 假设3行
                //rows=3
                //保存行高数组的下标值:
                //row:0,1,2
                val row = index % rows
                placeable.placeRelative(x = rowX[row], y = rowY[row])
                //第一列 x全部设置为0 下一列的x坐标要累加上前面元素的宽度
                //设置下一列的x坐标
                rowX[row] += placeable.width
            }
        }
    }
}

 我们来生成一些假数据去测试他

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(80, 100, 60, 70)
    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
}

这里pictrue还有color没用到

实际使用

@Composable
fun StaggeredGridBodyContent(modifier: Modifier = Modifier) {
    val dataList = getStaggeredList()
    Row(
        modifier = modifier
            .background(color = Color.LightGray)
            .padding(6.dp)
            .horizontalScroll(rememberScrollState())
            .verticalScroll(rememberScrollState()),
    ) {

        StaggeredGrid(modifier = Modifier, rows = 20) {
            for (data in dataList) {
                Chip(
                    modifier = Modifier
                        .padding(8.dp)
                        .height(data.height.dp), text = data.name
                )
            }

        }

    }
}

效果

能上下滑动,能左右滑动,但是不可以斜着滑动。宽度也可以千奇百怪一点

如果你有充分新的想象力就可以变得更有趣

完整代码

package com.anguomob.jecpack.activity.compose.layout

import androidx.compose.foundation.*
import androidx.compose.foundation.gestures.scrollable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Card
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.anguomob.jetpack.ui.theme.ComposeTheme

@Composable
fun StaggeredGrid(modifier: Modifier = Modifier, rows: Int = 3, content: @Composable () -> Unit) {
    Layout(modifier = modifier, content = content) { measurables, constraints ->
        //用于保存每行的宽度数值
        val rowWidths = IntArray(rows) { 0 }
        //用于保存每行的高度数值
        val rowHeightss = IntArray(rows) { 0 }

        val placeables = measurables.mapIndexed { index, measurable ->
            //测量每一个元素
            val placeable = measurable.measure(constraints)
            //计算每一行的宽度与高度
            //元素下标。假设总共11个元素
            //index 0 1 2 3 4 5 6 7 8 9 10
            //行数 假设3行
            //rows=3
            //保存行高数组的下标值:
            //row:0,1,2
            val row = index % rows
            //一行的宽度等于这一行所有元素宽度之和
            rowWidths[row] += placeable.width
            //一行的高度应该是最高的那个元素的高度
            rowHeightss[row] = kotlin.math.max(rowHeightss[row], placeable.height)
            placeable
        }
        //计算表格的高度
        //表格的宽度应该是所有行当中最宽的哪一行的宽度
        val width = rowWidths.maxOrNull() ?: constraints.minWidth
        //表格的高度应该是所有高度之和
        val height = rowHeightss.sumOf { it }
        //设置每一行的y坐标
        val rowY = IntArray(rows) { 0 }
        //索引从1开始,因为第一行y坐标为0,rows[0]=0
        for (i in 1 until rows) {
//            rowY[1] = rowY[0] + rowHeightss[0]
            rowY[i] = rowY[i - 1] + rowHeightss[i - 1]
        }

        layout(width, height) {
            val rowX = IntArray(rows) { 0 }
            placeables.forEachIndexed { index, placeable ->
                //index 0 1 2 3 4 5 6 7 8 9 10
                //行数 假设3行
                //rows=3
                //保存行高数组的下标值:
                //row:0,1,2
                val row = index % rows
                placeable.placeRelative(x = rowX[row], y = rowY[row])
                //第一列 x全部设置为0 下一列的x坐标要累加上前面元素的宽度
                //设置下一列的x坐标
                rowX[row] += placeable.width
            }
        }
    }
}

@Composable
fun Chip(modifier: Modifier, text: String, color: Color = MaterialTheme.colors.secondary) {
    //一个卡片 圆角,里面包含一个Row,第一列是Box 第二例是文本
    Card(
        modifier = modifier,
        //Hairline 发际线 默认一个像素
        border = BorderStroke(color = Color.Black, width = Dp.Hairline),
        shape = RoundedCornerShape(8.dp)
    ) {
        Row(
            modifier = Modifier.padding(start = 8.dp, top = 4.dp, end = 8.dp, bottom = 8.dp),
            verticalAlignment = Alignment.CenterVertically
        ) {
            Box(
                modifier = Modifier
                    .size(16.dp)
                    .background(color = color)

            )
            Spacer(modifier = Modifier.width(4.dp))
            Text(text)
        }
    }
}

private val rangeForRandom = (0..100000)


data class StaggeredItem(
    val name: String,
    val height: Int,
    val width: Int,
    val color: Color,
    val picture: String,
)


fun getStaggeredList(): MutableList<StaggeredItem> {
    val list = mutableListOf<StaggeredItem>()
    val heightList = listOf(80, 100, 60, 70)
    val widthList = listOf(40, 50, 60, 70)
    repeat(500) {
        val height = heightList.random()
        val width = widthList.random()
        val picture = "https://picsum.photos/seed/${rangeForRandom.random()}/500/$height"
        list.add(
            StaggeredItem(
                name = "name $it",
                height = height,
                width = width,
                color = Color(
                    red = (0..255).random(),
                    blue = (0..255).random(),
                    green = (0..255).random()
                ),
                picture = picture
            )
        )
    }
    return list
}


@Composable
fun StaggeredGridBodyContent(modifier: Modifier = Modifier) {
    val dataList = getStaggeredList()
    Row(
        modifier = modifier
            .background(color = Color.LightGray)
            .padding(6.dp)
            .horizontalScroll(rememberScrollState())
            .verticalScroll(rememberScrollState())
    ) {

        StaggeredGrid(modifier = Modifier, rows = 20) {
            for (data in dataList) {
                Chip(
                    modifier = Modifier
                        .padding(8.dp)
                        .height(data.height.dp)
                        .width(data.width.dp),
                    text = data.name,
                    color = data.color
                )
            }

        }

    }
}

 最后效果

猜你喜欢

转载自blog.csdn.net/mp624183768/article/details/125346146