Jetpack Compose 自定义布局

每个元素都会被要求根据父元素的约束来进行自我测量(类似传统 View 中的 MeasureSpec ),约束中包含了父元素允许子元素的最大宽度与高度和最小宽度与高度,当父元素想要强制子元素宽高为固定值时,其对应的最大值与最小值就是相同的。

Compose UI 不允许多次测量,每个子元素只允许被测量一次

这样做的好处是为了提高性能

使用 Layout Modifier

kotlin 复制代码
fun Modifier.customLayoutModifier(...) = Modifier.layout { measurable, constraints ->
  ...
})

当使用 layout 修饰符时,你传入的回调 lambda 需要包含两个参数:measurable、constraints

measurable:子元素的测量句柄,通过提供的api完成测量与布局过程

constraints: 子元素的测量约束,包括宽度与高度的最大值与最小值。

针对 Owl 项目中自定义布局进行讲解; 在 Onboarding.ktStaggeredGrid 方法

描述:该 demo 中自定义 view 通过自定义布局实现了横向滑动滑动瀑布流,如下图红色选中部分

kotlin 复制代码
@Composable
private fun StaggeredGrid(
    modifier: Modifier = Modifier,
    rows: Int = 3,
    content: @Composable () -> Unit
) {
    // 使用 Layout 绘制一组 ui
    Layout(
        content = content,
        modifier = modifier
    ) { measurables, constraints ->
        val rowWidths = IntArray(rows) { 0 } // Keep track of the width of each row
        val rowHeights = IntArray(rows) { 0 } // Keep track of the height of each row

        // Don't constrain child views further, measure them with given constraints
        val placeables = measurables.mapIndexed { index, measurable ->
        // 1. 测量单个组件的宽高,返回 Placeable 对象
            val placeable = measurable.measure(constraints)

            // Track the width and max height of each row
            val row = index % rows
            // 累计宽高,宽度累计,高度取最大值
            rowWidths[row] += placeable.width
            rowHeights[row] = max(rowHeights[row], placeable.height)

            placeable
        }
        // 2. 计算布局的宽高
        // Grid's width is the widest row
        val width = rowWidths.maxOrNull()?.coerceIn(constraints.minWidth, constraints.maxWidth)
            ?: constraints.minWidth
        // Grid's height is the sum of each row
        val height = rowHeights.sum().coerceIn(constraints.minHeight, constraints.maxHeight)

        // y co-ord of each row
        val rowY = IntArray(rows) { 0 }
        // 3. 统计 y 轴坐标点,绘制时使用
        for (i in 1 until rows) {
            rowY[i] = rowY[i - 1] + rowHeights[i - 1]
        }
        // 4. 重要!!绘制指定宽高的布局
        layout(width, height) {
            // x co-ord we have placed up to, per row
            val rowX = IntArray(rows) { 0 }
            placeables.forEachIndexed { index, placeable ->
                val row = index % rows
                // 5. 重要!! 绘制 ui
                placeable.place(
                    x = rowX[row],
                    y = rowY[row]
                )
                // 6. 修改坐标点,继续循环绘制
                rowX[row] += placeable.width
            }
        }
    }
}

Layout 需要填写三个参数:modifier,content,measurePolicy

modifier:由外部传入的修饰符,会决定该 UI 元素的 constraints

content:在 content 中声明所有子元素信息

measurePolicy :默认场景下只实现 measure 即可,上面示例中最后传入的 lambda 就是 measure 的实现。当你想要为你的 Layout Composable 适配 Intrinsics 时(官方中文翻译为固有特性测量),则需要重写 minIntrinsicWidthminIntrinsicHeightmaxIntrinsicWidthmaxIntrinsicHeight 方法,

Layout Modifier 不同的是,这里的 measurables 是一个 List,而 Layout Modifier 则只是一个 measurable,因为他将所有子元素看作了一个整体。

相关推荐
彩旗工作室27 分钟前
用 Supabase 打造统一认证中心:为多应用提供单点登录(SSO)
服务器·前端·数据库
EveryPossible42 分钟前
第一版代码
前端·javascript·css
ObjectX前端实验室1 小时前
【图形编辑器架构】渲染层篇 — 从 React 到 Canvas 的声明式渲染实现
前端·计算机图形学·图形学
java水泥工1 小时前
基于Echarts+HTML5可视化数据大屏展示-智慧消防大屏
前端·echarts·html5
杨超越luckly1 小时前
HTML应用指南:利用POST请求获取全国索尼体验型零售店位置信息
前端·arcgis·html·数据可视化·门店数据
ObjectX前端实验室1 小时前
【图形编辑器架构】节点树篇 — 从零构建你的编辑器数据中枢
前端·计算机图形学·图形学
ikun778g2 小时前
uniapp设置安全区
前端·javascript·vue.js·ui·uni-app
IT_陈寒2 小时前
React Hooks 实战:这5个自定义Hook让我开发效率提升了40%
前端·人工智能·后端
三月的一天2 小时前
React单位转换系统:设计灵活的单位系统与单位系统转换方案
前端·javascript·react.js
xiaoyan20153 小时前
2025最新款Electron38+Vite7+Vue3+ElementPlus电脑端后台系统Exe
前端·vue.js·electron