带你深入解析 Compose 的 Modifier 原理 -- Modifier、CombinedModifier

发布时间:2023年12月20日

Modifier 的含义


实际开发过程中,随处可见各种 Modifier,比如:

  Modifier.size()         // 尺寸
  Modifier.width()        // 宽度
  Modifier.height()       // 高度
  Modifier.padding()      // 间距
  Modifier.background()   // 背景
  Modifier.clip()         // 裁切
  Modifier.clickable()    // 点击
  ... ...

这个根部的 Modifier 是个啥?

interface Modifier {

    // 申明一个 Modifier 的伴生对象(单例对象)
    companion object : Modifier {
        // 里面全部都是最简单的实现
        override fun <R> foldIn(initial: R, operation: (R, Element) -> R): R = initial
        override fun <R> foldOut(initial: R, operation: (Element, R) -> R): R = initial
        override fun any(predicate: (Element) -> Boolean): Boolean = false
        override fun all(predicate: (Element) -> Boolean): Boolean = true
        override infix fun then(other: Modifier): Modifier = other
        override fun toString() = "Modifier"
    }

}

所以,如果你单写一个 Modifier,就可以获取到一个最简单的 Modifier 接口的对象(即伴生对象),它的作用就是作为一个起点。


我们现在做一个场景引入,比如自定义一个 Compose 函数:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            ComposeBlogTheme {
                custom()
            }
        }
    }
}

@Composable
fun custom() {
    Box(Modifier.size(40.dp).background(Color.Blue)) {}
}

效果如下:

在这里插入图片描述

这个场景很简单,在界面上添加了一个 Box,并且设定了背景色。如果现在我希望外部调用这个 custom 函数的时候可以从外部去设置它的透明度呢?我们可以这么写:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            ComposeBlogTheme {
                Custom(modifier = Modifier.alpha(0.5f))
            }
        }
    }
}

@Composable
fun Custom(modifier: Modifier) {
    Box(modifier.size(40.dp).background(Color.Blue)) {}
}

效果如下:

在这里插入图片描述

这样外部就可以通过传入一个 Modifier 去修改方块的尺寸了,但是这就存在一个问题了,外部只要调用 custom() ,就必须传入一个 Modifier,这就不合理了,相当于强制外部要加这个 Modifier 参数,而外部此时并不想修改尺寸,但也希望调用这个 custom 展示一个方块怎么办呢?

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            ComposeBlogTheme {
                Custom(modifier = Modifier.alpha(0.5f))  // 传了,custom 就用传入的 Modifier
                Custom()  // 不传,custom 就用默认的
            }
        }
    }
}

@Composable
fun Custom(modifier: Modifier = Modifier) {  // 设定一个默认的 Modifier,作为一个占位符使用
    Box(
        modifier
            .size(40.dp)
            .background(Color.Blue)) {}
}

这是一个很标准的写法。

我们看下 Box 这个 Compose 函数:

@Composable
inline fun Box(
    modifier: Modifier = Modifier,
    contentAlignment: Alignment = Alignment.TopStart,
    propagateMinConstraints: Boolean = false,
    content: @Composable BoxScope.() -> Unit
) 

换一个 Button 看看:

@Composable
fun Button(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    ... ...
) 

换一个 Column 看看:

@Composable
inline fun Column(
    modifier: Modifier = Modifier,
    verticalArrangement: Arrangement.Vertical = Arrangement.Top,
    horizontalAlignment: Alignment.Horizontal = Alignment.Start,
    content: @Composable ColumnScope.() -> Unit
) 

Compose 本身提供的函数参数也是这种标准写法,所以以后写自定义的 Compose 函数,我们也按照这种标准写法就会很方便。



Modifier 链


所谓的 Modifier 链 其实就是类似 Modifier.padding().background() 这样的链式调用。在实际开发过程中,这种链式调用对顺序的敏感度还是很强的,不同的调用顺序显示出来的结果会完全不一样。


接下来我们结合实际代码同步分析 Modifier 链的创建步骤。

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            ComposeBlogTheme {
                Box(Modifier.background(Color.Blue))
            }
        }
    }
}

对于 Modifier,我们之前说过它相当于一个白板,而 Modifier.background() 是 Modifier 的扩展函数:

fun Modifier.background(
    color: Color,
    shape: Shape = RectangleShape
) = this.then(     // this:就是 Modifier,这里又调用了 then()
    Background(
        color = color,
        shape = shape,
        inspectorInfo = debugInspectorInfo {
            name = "background"
            value = color
            properties["color"] = color
            properties["shape"] = shape
        }
    )
)

此时 this 指针指向的是伴生对象 Modifier,接下来我们看看 then() 做了什么。

then()

@Stable
interface Modifier {
    // 接口中的方法
    infix fun then(other: Modifier): Modifier =
        if (other === Modifier) this else CombinedModifier(this, other)

}
  1. 我们可以看到 then() 是有参数的,它的参数也是一个 Modifier 类型的,所以 Background() 也是一个 Modifier?
private class Background constructor(
    ... ...
) : DrawModifier, InspectorValueInfo(inspectorInfo) {

Background() 实际上就是一个 DrawModifier。

  1. then() 的作用:把调用的 Modifier(左边)和参数传入的 Modifier(右边)进行合并的。

if (other === Modifier) this:如果参数是一个最基本的 Modifier 伴生对象:companion object,则返回自己,即调用者。

比如如果我这么写:

Box(Modifier.background(Color.Blue).then(Modifier))
==>
就会直接返回 Box(Modifier.background(Color.Blue) 自身

那我们例子里的 Modifier.background() 返回的是什么呢?

Box(Modifier.background(Color.Blue)
// 它返回的是什么?
// 很明显是 then(Background()) 不满足 other === Modifier 的条件,所以应该走 CombinedModifier?

// 请注意:Modifier 是一个伴生对象,它内部覆写了 then() 方法

companion object : Modifier {
    ... ...
    // 传进来什么 Modifier,就返回什么 Modifier
    override infix fun then(other: Modifier): Modifier = other
}

所以 Modifier.background() 这种调用方本身就是一个伴生对象的情况,会走到它自己内部的 then() 方法,返回的是 Background()。

在这里插入图片描述

此时 Modifier 链的数据结构如下:

在这里插入图片描述

如果“调用者”和“传进来的参数”都不是 Modifier 伴生对象的话,就会走到下面一个条件。

else CombinedModifier(this, other):调用 CombinedModifier() 进行两个 Modifier 的融合,并返回自身。

比如我们现在再添加一个 size(40.dp):

Box(Modifier.background(Color.Blue).size(40.dp))

查看 size() 函数:

un Modifier.size(size: Dp) = this.then(
    SizeModifier(
        ... ...
    )
)

这个时候的 this 就是 Background() 了(也就是 DrawModifier),而 then() 内部的参数又是一个 SizeModifier,这个时候就要用到 CombinedModifier 进行融合了。

class CombinedModifier(
    internal val outer: Modifier,
    internal val inner: Modifier
) : Modifier

此时 Modifier 链的数据结构如下:

在这里插入图片描述

比如我们现在再添加一个 Padding(10.dp):

Box(Modifier.background(Color.Blue).size(40.dp).padding(10.dp))

查看 padding() 函数:

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

这个时候的 this 指向的是 CombinedModifier 实例,而 then() 内部的参数又是一个 PaddingModifier,这个时候就又要用到 CombinedModifier 进行融合了。

此时 Modifier 链的数据结构如下:

在这里插入图片描述

所以你会发现,目前我们看到的 Modifier 函数都有一个 then() 作为最外层调用,它 可以作为一个套子,打造一个一环套一环的链条。


接下来我们继续分析,查看 CombinedModifier 函数源码:

class CombinedModifier(
    private val outer: Modifier,
    private val inner: Modifier
) : Modifier

CombinedModifier 连接的两个 Modifier 分别存储在 outerinner 中,Compose 对 Modifier 的遍历,就是从外(outer)到内(inner)一层层访问。需要注意的是, outerinnner 字段都被 private 关键字申明,意味着不能被外部直接访问,但官方为我们提供了 foldOut()foldIn() 专门用来遍历 Modifier 链。

class CombinedModifier(
    private val outer: Modifier,
    private val inner: Modifier
) : Modifier {
    override fun <R> foldIn(initial: R, operation: (R, Modifier.Element) -> R): R =
        inner.foldIn(outer.foldIn(initial, operation), operation)    // 正向遍历

    override fun <R> foldOut(initial: R, operation: (Modifier.Element, R) -> R): R =
        outer.foldOut(inner.foldOut(initial, operation), operation)  // 反向遍历

    override fun any(predicate: (Modifier.Element) -> Boolean): Boolean =
        outer.any(predicate) || inner.any(predicate)    // 或运算

    override fun all(predicate: (Modifier.Element) -> Boolean): Boolean =
        outer.all(predicate) && inner.all(predicate)    // 与运算
}

例如:

Modifier.background(Color.Blue).size(40.dp).padding(10.dp)

foldIn: 正向遍历 Modifier 链,DrawModifier -> SizeModifier -> PaddingModifier

foldOut: 反向遍历 Modifier 链,PaddingModifier -> SizeModifier -> DrawModifier

foldIn() 解析

我们看下 foldIn 源码:

fun <R> foldIn(initial: R, operation: (R, Element) -> R): R

它有两个参数:

  1. initial:初始值,这个初始值未必一定是 Modifier
  2. operation:每遍历到一个 Modifier 时的回调,这个 lambda 又有两个参数,R类型与 Element类型

foldIn 方法在遍历当前 Modifier 时执行的 operation 的 返回值 将作为链中下一个 Modifier 的 operation 的 R 类型 参数传入

foldOut() 解析

foldIn() 相反, 我们就不再单独展开分析。



Modifier.Element


在 Modifier 整个体系里面,有很多 Modifier 接口的子接口和实现类,除了我们之前提过的 companion object 伴生对象和 CombinedModifier 类之外,其他所有的 Modifier,不管是接口还是实现类,全部都直接或者间接的继承了 Modifier 的另外一个子接口:Element


jbVW8S.png


interface Element : Modifier {
        /**
         * 给定一个初始对象,再给出一个算法,第一个 Modifier 用到这个初始对象上会有第一个返回结果
         * 第二个 Modifier 用在第一个返回结果并返回一个结果,然后一层套一层。
         */
        override fun <R> foldIn(initial: R, operation: (R, Element) -> R): R =
            operation(initial, this)

        // 与 foldIn 相反
        override fun <R> foldOut(initial: R, operation: (Element, R) -> R): R =
            operation(this, initial)

        // 检查 CombinedModifier 内部所有 Modifier 是否有一个满足条件
        override fun any(predicate: (Element) -> Boolean): Boolean = predicate(this)

        // 检查 CombinedModifier 内部所有 Modifier 是否全部都满足条件
        override fun all(predicate: (Element) -> Boolean): Boolean = predicate(this)
    }
文章来源:https://blog.csdn.net/pepsimaxin/article/details/135109945
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。