当前位置: 首页 > news >正文

Compose笔记(七)--Modifier

       这一节主要了解一下Compose中Modifier,它本质上是一个接口,用于描述如何修改一个可组合项(Composable)的外观和行为。它可以看作是一系列修改操作的集合,这些操作可以改变组件的大小、位置、边距、背景、点击事件等属性。

1. 布局调整

Text("Hello", modifier = Modifier.width(200.dp).height(50.dp))

2. 外观修改

设置背景
Text("Hello", modifier = Modifier.background(Color.Blue))
添加边框:通过 border 可以为组件添加边框。
Text("Hello", modifier = Modifier.border(1.dp, Color.Red))

3. 交互处理

Text("Click me", modifier = Modifier.clickable {
    // 处理点击事件
})

栗子:

Composable
fun CustomLayoutModifierExample() {
    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.LightGray)
    ) {
        Text(
            text = "This is a centered text with max width",
            modifier = Modifier
                .customLayout { measurable, constraints ->
                    // 限制最大宽度为父容器宽度的一半
                    val maxWidth = constraints.maxWidth / 2
                    val childConstraints = constraints.copy(maxWidth = maxWidth)
                    val placeable = measurable.measure(childConstraints)

                    // 在父容器中居中
                    layout(placeable.width, placeable.height) {
                        placeable.placeRelative(
                            (constraints.maxWidth - placeable.width) / 2,
                            (constraints.maxHeight - placeable.height) / 2
                        )
                    }
                }
                .background(Color.Blue)
                .padding(16.dp)
        )
    }
}

fun Modifier.customLayout(
    measure: MeasureScope.(Measurable, Constraints) -> MeasureResult
) = this.then(object : LayoutModifier {
    override fun MeasureScope.measure(
        measurable: Measurable,
        constraints: Constraints
    ): MeasureResult {
        return measure(measurable, constraints)
    }
})

分析:创建一个 Modifier,使组件在其父容器中居中并限制最大宽度,实现文本在父容器中居中。

@Composable
fun CustomDrawModifierExample() {
    Box(
        modifier = Modifier
            .size(200.dp)
            .background(Color.LightGray)
            .drawWithDashedBorder(
                color = Color.Red,
                strokeWidth = 4.dp,
                dashWidth = 10.dp,
                gapWidth = 5.dp
            )
    )
}

fun Modifier.drawWithDashedBorder(
    color: Color,
    strokeWidth: Dp,
    dashWidth: Dp,
    gapWidth: Dp
) = this.then(object : DrawModifier {
    override fun ContentDrawScope.draw() {
        val strokeWidthPx = strokeWidth.toPx()
        val dashWidthPx = dashWidth.toPx()
        val gapWidthPx = gapWidth.toPx()

        // 绘制虚线边框
        drawIntoCanvas { canvas ->
            val paint = Paint().apply {
                this.color = color
                this.style = PaintingStyle.Stroke
                this.strokeWidth = strokeWidthPx
                this.pathEffect = PathEffect.dashPathEffect(
                    floatArrayOf(dashWidthPx, gapWidthPx),
                    0f
                )
            }

            val path = androidx.compose.ui.graphics.Path().apply {
                addRect(
                    Rect(
                        strokeWidthPx / 2,
                        strokeWidthPx / 2,
                        size.width - strokeWidthPx / 2,
                        size.height - strokeWidthPx / 2
                    )
                )
            }

            canvas.drawPath(path, paint)
        }
    }
})

分析:实现创建一个 Modifier,在组件周围绘制一个虚线边框,虚线的宽度和间距可以自定义。

@Composable
fun CustomInteractionModifierExample() {
    var text by remember { mutableStateOf("Double tap here") }

    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.LightGray)
            .detectDoubleTap {            
                text = "Double tapped!"
            }
                ,
        contentAlignment = Alignment.Center
    ) {
        Text(text = text)
    }
}

fun Modifier.detectDoubleTap(onDoubleTap: () -> Unit
                             ) = this.pointerInput(Unit) {
    detectTapGestures(
        onDoubleTap = { onDoubleTap() }
    )
}

分析:实现当用户双击Box区域时,文本会更新为 "Double tapped!"

注意: 1 避免重复创建 Modifier 实例:在循环或频繁调用的场景中,重复创建相同的 Modifier 会增加内存开销。可以将常用的 Modifier 提取为常量或使用 remember 进行缓存。2  Modifier 顺序,Modifier 的顺序会影响最终的效果。例如,先设置 padding 再设置 background 和先设置 background 再设置 padding 会有不同的视觉效果。一般来说,影响布局的 Modifier 应放在前面。3 避免在 Modifier 中进行复杂计算,Modifier 中的计算应尽量简单,避免在其中进行复杂的逻辑处理或耗时操作。可以将复杂计算提前完成,并将结果传递给 Modifier。

源码

1.Modifier接口定义
        Modifier是一个接口,它定义了 then方法用于组合多个Modifier,同时包含一个静态的Empty实例,表示空的Modifier。

interface Modifier {
    infix fun then(other: Modifier): Modifier

    companion object : Modifier {
        override infix fun then(other: Modifier): Modifier = other
        val Empty = this
    }
}

分析:

then 方法:该方法接收另一个 Modifier 作为参数,返回一个新的 Modifier,这个新的 Modifier 是当前 Modifier 和传入 Modifier 的组合。
companion object:Empty 实例是一个特殊的 Modifier,它作为组合的起始点,当调用 Empty.then(other) 时,直接返回 other。

2. Modifier 组合原理
       Modifier 的组合是通过 then 方法实现的。在实际使用中,我们通常会使用扩展函数来创建和组合 Modifier。例如,padding 扩展函数:

fun Modifier.padding(all: Dp): Modifier = this.then(
    PaddingModifier(
        PaddingValues(all)
    )
)

分析:

this 表示当前的 Modifier。
PaddingModifier 是一个实现了 Modifier.Element 接口的类,用于处理内边距的逻辑。
then 方法将当前 Modifier 和 PaddingModifier 组合成一个新的 Modifier。

3.Modifier.Element接口
       Modifier.Element是 Modifier的一个子接口,用于定义具体的修改逻辑。例如PaddingModifier类实现了Modifier.Element接口:

private class PaddingModifier(
    private val padding: PaddingValues
) : Modifier.Element {
    override fun <R> foldIn(initial: R, operation: (R, Modifier.Element) -> R): R {
        // 在布局之前处理,这里可以进行一些计算
        return operation(initial, this)
    }

    override fun <R> foldOut(initial: R, operation: (Modifier.Element, R) -> R): R {
        // 在布局之后处理
        return operation(this, initial)
    }

    override fun toString(): String = "PaddingModifier(padding=$padding)"
}

分析:

foldIn 方法:在布局过程中,从前往后依次处理每个 Modifier.Element,可以在这个方法中进行布局前的计算。
foldOut 方法:在布局过程中,从后往前依次处理每个 Modifier.Element,可以在这个方法中进行布局后的处理。

4. 布局过程中的Modifier处理
       在Compose的布局过程中,LayoutNode 类负责管理组件的布局,它会调用 Modifier的foldIn和foldOut方法来处理每个Modifier.Element。简化版的布局过程如下:

class LayoutNode {
    var modifier: Modifier = Modifier.Empty

    fun measure(constraints: Constraints): Placeable {
        var current = modifier.foldIn(Unit) { _, element ->
            // 处理每个 Modifier.Element
            if (element is PaddingModifier) {
                // 处理内边距
            }
        }
        // 进行实际的测量
        return Placeable(/* ... */)
    }

    fun layout(width: Int, height: Int) {
        modifier.foldOut(Unit) { element, _ ->
            // 在布局之后处理每个 Modifier.Element
        }
    }
}

分析:

measure方法:在测量阶段,调用foldIn方法从前往后处理每个 Modifier.Element,可以根据Modifier的要求调整测量的约束条件。
layout方法:在布局阶段,调用foldOut方法从后往前处理每个 Modifier.Element,可以根据Modifier的要求调整组件的位置和大小。

5. 链式调用的实现
        通过 then 方法和扩展函数,我们可以实现 Modifier 的链式调用。例如:

val modifier = Modifier
   .padding(16.dp)
   .background(Color.Red)

分析:

调用 padding(16.dp)时,实际上是调用了Modifier的扩展函数,返回一个新的Modifier,这个新的Modifier是当前Modifier和PaddingModifier 的组合。
接着调用background(Color.Red),同样返回一个新的Modifier,这个新的Modifier是上一个Modifier和BackgroundModifier的组合。

相关文章:

  • 《算法笔记》9.6小节 数据结构专题(2)并查集 问题 C: How Many Tables
  • EasyExcel构建复杂多级表头
  • 网络安全员证书
  • go类(结构体)和对象
  • windows下玩转vllm:在wsl下安装vllm
  • 高速光耦在通信行业的应用(四) | 1Mbps通信光耦的应用
  • sentinel详细使用教学
  • 两分种解决:xshell终端delete键无效
  • c++中的静态多态和动态多态简介
  • 性能测试【Perfdog】
  • 接口测试工具:postman详解
  • CAM350_安装
  • 【Linux高级IO】Linux多路转接:深入探索poll与epoll的奥秘
  • Ollama的底层实现原理分析
  • 《浔川AI翻译v6.1.1版本推迟上线公告》
  • C++类和对象
  • 算法day4 dfs搜索2题
  • 【机房——LCA】
  • 小米火龙CPU和其他几代温度太高的CPU是由谁代工的
  • 卢卡斯定理判断组合数奇偶(Codeforces Round 1006 (Div. 3)——F)
  • ui网站建设站评价/互联网宣传推广
  • 多语言网站是怎么做的/指数基金定投技巧
  • 常用网站开发技术和工具/千锋教育学费
  • 佛山网站建设哪家评价高/宁德市委书记
  • 网站建设费一般是什么费用/凡科建站的优势
  • 传统的网站开发模式和mvc/南宁seo结算