一、Modifier简单使用
我们先拿XML中的FrameLayout做下对比,如下,我们在xml文件中定义了一个 宽度填充满父容器,高度200dp,背景为黑色,内容边距为16dp的 FrameLayout:
<FrameLayout
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#000000"
android:padding="16dp" >
<-- 可配置子级元素 -->
</FrameLayout>
那么在Compose中如何实现这样的UI呢?直接来看结果,了解下Modifier是如何配合Composable函数实现的:
@Composable
fun BoxDemo() {
Box(
modifier = Modifier
.fillMaxWidth()
.height(200.dp)
.background(Color.Black)
.padding(16.dp),
) {
//可配置子级元素
}
}
Modifier是有顺序的,不同执行顺序会有不同效果
二、Modifier的链式结构
Modifier是个接口,他有三个直接的实现类
- 伴生对象Modifier
- 内部子接口Modifier.Element
- 像size操作符,对应的就是SizeModifier,其实现的接口是LayoutModifier,LayoutModifier的父接口就是Modifie.Element
- CombinedModifier
- Compose内部维护的数据结构,用于连接Modifier链中的每个Modifier结点
的每个操作符都是对应一个
Modifier.Element
通过then函数将Modifier添加到Modifier的链条里
- 如果这个是第一个Modifier,那就直接加这个Modifier
- 如果Modifier的链条size>0,那把当前的Modifier和要添加的Modifier组合进CombinedModifier的数据结构中
每个Modifier(CombinedModifier)都会实现foldIn,通过foldIn来遍历Modifier链条中的所有成员
Modifier
.size(100.dp)
fun Modifier.size(size: Dp) = this.then( // 关键方法
SizeModifier(
...
)
)
interface Modifier {
infix fun then(other: Modifier): Modifier =
if (other === Modifier) this else CombinedModifier(this, other)
}
class CombinedModifier(
private val outer: Modifier,
private val inner: Modifier
) : Modifier
对于以下代码
Modifier
.size(100.dp)
.background(Color.Red)
.padding(10.dp)
.pointerInput(Unit) {
...
}
此时Modifier链的数据结构
当Modifier链的长度大于等于2时,返回的Modifier实际上是一个CombinedModifier实例
foldIn():正向遍历Modifier链,SizeModifier-> Background -> PaddingModifier
foldOut():反向遍历 Modifier 链, 、PaddingModifier -> Background ->SizeModifier
foldOut()与foldIn()是需要传递参数的。这里涉及到两个参数initial,operation
fun <R> foldIn(initial: R, operation: (R, Element) -> R): R
fun <R> foldOut(initial: R, operation: (Element, R) -> R): R
initial:初始值
operation:每遍历到一个Modifier时的回调,这个lambda又有两个参数,R类型与Element类型
foldIn方法类似于for (int i = initial; ; operation()) 。设置initial参数类似为i设置初始值,而operation返回值将作为值的更新。
也就是说遍历当前Modifier时执行的operation的返回值将作为链中下一个Modifier的operation的R类型参数传入。这么说可能比较晦涩难懂,在这里简单举个例子,比如说我们希望统计Modifier链中有Modifier的数量。
val modifier = Modifier
.size(100.dp)
.background(Color.Red)
.padding(10.dp)
.pointerInput(Unit) {
}
val result = modifier.foldIn<Int>(0) { currentIndex, element ->
Log.d("compose_study", "index: $currentIndex , element :$element")
currentIndex + 1
}
设计者一定会将一系列的Modifier设计成一个类似链表的结构,并且希望我们从Modifier的 companion实现开始进行构建链表。
如果结合注释,我们可以获知:Modifier会通过 then 组成一个链表,并且 any 和 all 是对链表的元素运行判断表达式,foldIn,foldOut 则会对链表的元素执行operation。
Modifier就是类似装饰器模式,但不是
类似于Rx装饰器模式那样(类似,但是不是准确有待验证)
修饰函数并不是简单修改了某个组件内部的参数,而是给这个组件 套上了一层又一层的修饰器,
所以会有顺序的问题
每个Modifier都是调用了Modifiier的then方法,把这个Modifier扩展函数对应的一个Modifier的实现类通过then与原Modifier组合起来
CombinedModifier 持有了我们新增的修饰器和原有的修饰器,并且将其组合为一个新的 Modifier。
可以看到,Modifier 的实现非常类似于一个链表,当我们给一个组件添加一个修饰函数时,它会创建一个 CombinedModifier 将 旧的和新的Modifier组合在一起,合成为一个单独的 Modifie
fun Modifier.padding(all: Dp) =
this.then(
PaddingModifier(
//参数,这里省略
)
)
infix fun then(other: Modifier): Modifier =
if (other === Modifier) this else CombinedModifier(this, other)
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)
override fun equals(other: Any?): Boolean =
other is CombinedModifier && outer == other.outer && inner == other.inner
override fun hashCode(): Int = outer.hashCode() + 31 * inner.hashCode()
override fun toString() = "[" + foldIn("") { acc, element ->
if (acc.isEmpty()) element.toString() else "$acc, $element"
} + "]"
}
三、FoldIn流程
compose中Modifier链会使用foldOut方法 进行遍历从而生成LayoutNodeWrapper链
interface Modifier {
//...
interface Element : Modifier {
override fun <R> foldIn(initial: R, operation: (R, Element) -> R): R =
operation(initial, this)
override fun <R> foldOut(initial: R, operation: (Element, R) -> R): R =
operation(this, initial)
override fun any(predicate: (Element) -> Boolean): Boolean = predicate(this)
override fun all(predicate: (Element) -> Boolean): Boolean = predicate(this)
}
}
CombinedModifier中的foldIn:
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)
}
这里举一个栗子来看 foldIn 和 foldOut 的递归:
class A : Modifier.Element
class B : Modifier.Element
class C : Modifier.Element
fun Modifier.a() = this.then(A())
fun Modifier.b() = this.then(B())
fun Modifier.c() = this.then(C())
那么 Modifier.a().b().c() 的到的是什么呢?为了看起来直观点,我们 以 CM 代指 CombinedModifier
CM (
outer = CM (
outer = A(),
inner = B()
),
inner = C()
)
Modifier.a().b().c().foldIn(initial, operation)
所得到的执行过程为:
val ra = operation.invoke(initial,A())
val rb = operation.invoke(ra,B())
return operation.invoke(rb,C())
结合前面的知识,这里举个例子来说明operation和initial两者的关系
val initial = StringBuilder()
val operation: (StringBuilder, Element) -> StringBuilder = { builder, e ->
builder.append(e.toString()).append(";")
builder
}
可以看出,operation 就是不断将每个Element的内容加到initial中,并且返回值就是这个添加了自身Element内容的initial,作为下一个operation的initial
Modifier链后续还会使用foldOut方法 进行遍历从而生成LayoutNodeWrapper链