Compose Modifier

一、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就是类似装饰器模式,但不是

概述 - Jetpack Compose

类似于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链

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值