Kotlin Compose 自定义布局 StaggeredGrid
Posted 安果移不动
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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)
效果
再进行分析这个大的布局
经过一系列的计算 父布局就是这样
@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
)
最后效果
以上是关于Kotlin Compose 自定义布局 StaggeredGrid的主要内容,如果未能解决你的问题,请参考以下文章
Kotlin Compose 自定义 CompositionLocalProvider CompositionLocal
Kotlin 元编程之 KSP 实战:通过自定义注解配置Compose导航路由