一、Modifier简单使用

我们先拿XML中的FrameLayout做下对比,如下,我们在xml文件中定义了一个 宽度填充满父容器,高度200dp,背景为黑色,内容边距为16dp的 FrameLayout:

    <FrameLayoutandroid: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.inneroverride 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.Elementfun 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链

Compose Modifier相关推荐

  1. android Compose Modifier介绍

    常用修饰符 1.Modifier.size Image(painter = painterResource(id = R.mipmap.head),contentDescription = null, ...

  2. Jetpack Compose 从入门到入门(四)

    本篇开始介绍Jetpack Compose 中常用的组件.有一部分之前的文章中也出现过,今天详细说明一下. 1. Text 日常最常用的应该就是显示文字,所以有必要说一下Text控件.首先源码如下: ...

  3. Android Weekly - 42 : 打铁还需自身硬

    技术文章 Android Native | 内存问题的终极武器 --MTE https://juejin.cn/post/7013595058125406238 Android R 常见 GC 类型与 ...

  4. Jetpack Compose中的Modifier

    Modifier的基本使用 Modifier修饰符是Jetpack Compose中用来修饰组件的,提供常用的属性,写布局时几乎所有Composable组件的大部分属性都可以用Modifier 来修饰 ...

  5. 详解Jetpack Compose中的Modifier修饰符

    前言 本文将会介绍Jetpack Compose中的Modifier.在谷歌官方文档中它的描述是这么一句话:Modifier元素是一个有序.不可变的集合,它可以往Jetpack Compose UI元 ...

  6. JetPack Compose之Modifier修饰符

    前言 在Compose中,每一个组件都是带有@Compose注解的函数,被称为Composable.Compose已经预置了很多的Compose UI组件,这些组件都是基于Material Desig ...

  7. KMM+Compose 开发一个Kotlin多平台应用

    前言 现在跨平台开发框架有很多,比如H5类型,RN,Flutter等,而Kotlin多平台+Compose跨平台ui可能也是未来一种好用的开发框架 ps:后文KMM都是指Kotlin多平台框架,而不是 ...

  8. Compose 手势事件:防止重复点击,双击,长按,全局触摸隐藏键盘

    前言 JetPack Compose (后续简称compose) release版已经出来了三四个月了,虽然没正式版之前也学过几次,但一直没有机会用,在加上api的变更,导致之前学的都忘完了,现在终于 ...

  9. Compose Multiplatform 正式官宣,与 Flutter 必有一战?

    作者 | fundroid 来源 | AndroidPub 7月底 Compose for Android 1.0 刚刚发布,紧接着 8月4日 JetBrains 就宣布了 Compose Multi ...

  10. Compose 1.0 即将发布,你准备好了吗?

    作者 | fundroid       责编 | 欧阳姝黎 Compose 于2019年的Google IO大会首次发布,当时感觉前景并不乐观,想推翻已存在10年之久的现有视图体系谈何容易,更何况将来 ...

最新文章

  1. FAQ系列 | 如何保证主从复制数据一致性(转)
  2. python处理csv文件缺失值_Python:如何处理CSV中的缺失值?
  3. 调试一个C#研究生管理信息系统源码
  4. 文件复制函数的效率比较
  5. IntelliJ IDEA 删除了快捷键后,如何找回来?
  6. 页面跳转多种方法(加传参)
  7. 存储对手机性能的影响
  8. python创建应用程序窗口_如何设置pythonwxwidgets应用程序窗口的类名?
  9. Java 即将迎来转折点
  10. 遇到了火狐扩展全部丢失的问题
  11. 继电器设备改造需要的设备清单
  12. Hadoop-HDFS原理及操作(小实验)
  13. vue在线预览word踩坑日记
  14. linux开机启动grub rescue,Ubuntu 开机出现 grub rescue 的模式下修复
  15. 关于Android自启动管理的相关知识点
  16. .ipynb文件怎么打开_Mac应用程序无法打开提示不明开发者或文件损坏怎么对待呢?
  17. 接入层交换机和汇聚层交换机的选择及连接方案
  18. 盲子强巴(连载)二、
  19. 极其简单的 使用IDEA 中 实现springboot 热部署 (spring boot devtools版)
  20. 【区块链技术工坊29期实录】小包总:SERO基于零知识证明的隐私保护公链解决方案

热门文章

  1. 很短,很文艺,很唯美的英语美句
  2. mysql创建数据库表bank_数据库创建表fk
  3. 事后诸葛亮(追光的人)
  4. 单位换算:潜热通量(w/m2)转换为蒸散发(mm/s)
  5. 有些事现在不做,一辈子都不会做了
  6. R语言通过WinBUGS对MGARCH和MSV模型进行贝叶斯估计和比较
  7. matlab quadratic,请教几个quadratic programming的问题
  8. AtCoder Beginner Contest 224题解 A-G
  9. Android内存优化大全(二)
  10. Python-Django毕业设计老薛男生服装网(程序+LW)