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)
}
- 我们可以看到
then()
是有参数的,它的参数也是一个 Modifier 类型的,所以 Background() 也是一个 Modifier?
private class Background constructor(
... ...
) : DrawModifier, InspectorValueInfo(inspectorInfo) {
Background() 实际上就是一个 DrawModifier。
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 分别存储在 outer
与 inner
中,Compose 对 Modifier 的遍历,就是从外(outer)到内(inner)一层层访问。需要注意的是, outer
与 innner
字段都被 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
它有两个参数:
- initial:初始值,这个初始值未必一定是 Modifier
- operation:每遍历到一个 Modifier 时的回调,这个 lambda 又有两个参数,R类型与 Element类型
foldIn 方法在遍历当前 Modifier 时执行的 operation 的 返回值
将作为链中下一个 Modifier 的 operation 的 R 类型 参数传入
。
foldOut() 解析
与 foldIn()
相反, 我们就不再单独展开分析。
Modifier.Element
在 Modifier 整个体系里面,有很多 Modifier 接口的子接口和实现类,除了我们之前提过的 companion object
伴生对象和 CombinedModifier
类之外,其他所有的 Modifier,不管是接口还是实现类,全部都直接或者间接的继承了 Modifier
的另外一个子接口:Element
。
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)
}