ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Compose中的自定义布局(四)

2021-07-15 18:30:55  阅读:427  来源: 互联网

标签:Compose layout 自定义 val 布局 fun Modifier placeable constraints


文章目录

一、前言

在实际开发中会出于各种原因进行自定义布局,所以这里简单记录下使用Compose进行自定义布局的方式

二、使用布局修饰符

本段参考代码是google的代码

我们可以使用layout修饰符来元素的测量和布局方式,大概方式如下:

fun Modifier.customLayoutModifier(...) = Modifier.layout { measurable, constraints ->
  ...
})

不过实际应用中通常使用以下写法:

fun Modifier.customLayoutModifier(...) =
    this.layout { measurable, constraints ->
        ...
    })

比如想控制显示的Text顶部到第一行基线的位置,示例如下:

fun Modifier.firstBaselineToTop(
    firstBaselineToTop: Dp
) = layout { measurable, constraints ->
    // Measure the composable
    val placeable = measurable.measure(constraints)

    // 检查是否包含基线,如果不包含则会引发异常
    check(placeable[FirstBaseline] != AlignmentLine.Unspecified)
    val firstBaseline = placeable[FirstBaseline]

    // Height of the composable with padding - first baseline
    val placeableY = firstBaselineToTop.roundToPx() - firstBaseline
    val height = placeable.height + placeableY
    layout(placeable.width, height) {
        // Where the composable gets placed
        placeable.placeRelative(0, placeableY)
    }
}

三、创建自定义布局

layout 修饰符仅更改调用可组合项。如需测量和布置多个可组合项,请改用 Layout 可组合项。此可组合项允许您手动测量和布置子项。ColumnRow 等所有较高级别的布局都使用 Layout 可组合项构建而成。大都数自定义布局遵循以下方式:

@Composable
fun MyBasicColumn(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit
) {
    Layout(
        modifier = modifier,
        children = content
    ) { measurables, constraints ->
        // measure and position children given constraints logic here
    }
}

比如我们自定义一个Column布局,示例如下:

@Composable
fun MyBasicColumn(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit
) {
    Layout(
        modifier = modifier,
        content = content
    ) { measurables, constraints ->
        // Don't constrain child views further, measure them with given constraints
        // List of measured children
        val placeables = measurables.map { measurable ->
            // Measure each children
            measurable.measure(constraints)
        }

        // Set the size of the layout as big as it can
        layout(constraints.maxWidth, constraints.maxHeight) {
            // Track the y co-ord we have placed children up to
            var yPosition = 0

            // Place children in the parent layout
            placeables.forEach { placeable ->
                // Position item on the screen
                placeable.placeRelative(x = 0, y = yPosition)

                // Record the y co-ord placed up to
                yPosition += placeable.height
            }
        }
    }
}

四、固有特性测量

一般来说,在自定义布局中使用默认测量方式就可以了,但是有时候可能并不能满足需求。因此要指定自定义 Layout 的固有特性测量,则在创建该布局时替换 MeasurePolicy的 minIntrinsicWidthminIntrinsicHeightmaxIntrinsicWidthmaxIntrinsicHeight

代码结构如下:

@Composable
fun MyCustomComposable(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit
) {
    return object : MeasurePolicy {
        override fun MeasureScope.measure(
            measurables: List<Measurable>,
            constraints: Constraints
        ): MeasureResult {
            // Measure and layout here
        }

        override fun IntrinsicMeasureScope.minIntrinsicWidth(
            measurables: List<IntrinsicMeasurable>,
            height: Int
        ) = {
            // Logic here
        }

        // Other intrinsics related methods have a default value,
        // you can override only the methods that you need.
    }
}

而在创建自定义 layout 修饰符时,替换 LayoutModifier 界面中的相关方法。

fun Modifier.myCustomModifier(/* ... */) = this.then(object : LayoutModifier {

    override fun MeasureScope.measure(
        measurable: Measurable,
        constraints: Constraints
    ): MeasureResult {
        // Measure and layout here
    }

    override fun IntrinsicMeasureScope.minIntrinsicWidth(
        measurable: IntrinsicMeasurable,
        height: Int
    ): Int = {
        // Logic here
    }

    // Other intrinsics related methods have a default value,
    // you can override only the methods that you need.
})

五、分析修饰符

这里分析下Modifier.padding的原理,代码如下(以下代码源自Google,不过已经修改为更易懂的方式):

// How to create a modifier
@Stable
fun Modifier.padding(all: Dp) =
    this.then(
        PaddingModifier(start = all, top = all, end = all, bottom = all, rtlAware = true)
    )

// Implementation detail
private class PaddingModifier(
    val start: Dp = 0.dp,
    val top: Dp = 0.dp,
    val end: Dp = 0.dp,
    val bottom: Dp = 0.dp,
    val rtlAware: Boolean,
) : LayoutModifier {

    override fun MeasureScope.measure(
        measurable: Measurable,
        constraints: Constraints
    ): MeasureResult {

        val horizontal = start.roundToPx() + end.roundToPx() //获取padding的横向长度
        val vertical = top.roundToPx() + bottom.roundToPx() //获取padding的垂直长度

        // val placeable = measurable.measure(constraints.offset(horizontal, vertical)) //偏移horizontal、vertical距离后进行测量,偏移只会更改内容位置,不会影响测量大小,因为下面已经进行偏移了,所以可以不用这么麻烦
 		val placeable = measurable.measure(constraints)
        val width = constraints.constrainWidth(placeable.width + horizontal)
        val height = constraints.constrainHeight(placeable.height + vertical)
        return layout(width, height) {//定义父布局宽高
            if (rtlAware) {
                placeable.placeRelative(start.roundToPx(), top.roundToPx()) //将组件在现有位置上进行移动,该移动是在布局里面,所以并不会超出布局宽高
            } else {
                placeable.place(start.roundToPx(), top.roundToPx())
            }
        }
    }
}

这里面有一个有意思的问题,就是调用placeable.placeRelative偏移后为什么不会超出设置的宽高?

这里解释下Placeable,文档上解释的意思是:Placeable对应于可以由其父布局定位的子布局。大多数PlaceableMeasurable.measure调用的结果。Placeable不应该在测量调用之间存储。其中placeable.width是父布局所需要留出的宽度,placeable.height是父布局所需要留出的高度。而placeable.measuredWidth才是控件真正的测量宽度,placeable.measuredHeight是控件真正的测量高度。因此调用placeable.placeRelative函数并不会导致组件超出布局。

六、参考链接

  1. Compose中的自定义布局:

    https://developer.android.google.cn/jetpack/compose/layout#decoupled

  2. compose的codelabs

    https://developer.android.google.cn/codelabs/jetpack-compose-layouts#6

  3. Placeable的类说明

    https://developer.android.google.cn/reference/kotlin/androidx/compose/ui/layout/Placeable?hl=en

标签:Compose,layout,自定义,val,布局,fun,Modifier,placeable,constraints
来源: https://blog.csdn.net/Mr_Tony/article/details/118765859

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有