modifier: Modifier = Modifier 的用法
Google 推荐在自定义一个Compose的时候,第一个有默认参数值的类型 要用 modifer ,例如:
有人可能会问为啥要这样推荐呢,因为第一个有默认参数类型的 参数 在实际调用的时候 可以不写 xxx=yyy
就是让调用者方便一点 仅此而已, 接着看上面红框中的代码 ,有人觉得 这个代码好奇怪啊
因为我点进去以后 发现这个Modifier是一个对象啊,对象也可以做参数类型了?
仔细看看,其实不然
这个Modifier本质上 他首先是一个接口,在这个接口之中,我们定义了一个
companion object : Modifier
Modifier的伴生对象,注意这个对象是实现了Modifier接口的,只是这个对象名字和interface的名字一样。
这个语法糖在很多地方都有调用,比如kotlin的协程中 就有这个东西,有兴趣的可以自己看一下,
所以再看一下这个写法:
第一个参数是接口,默认值是一个名为Modifier的半生对象 他实现了Modifer 这个接口
如何理解Modifier的链式调用
看下面这行代码
Modifier.background(Color.Green)
我们点进去看以后可以得到下图:
通过这张图,我们可以得到如下若干个信息
1.backgroun是Mofifer接口的一个扩展函数 2.这个扩展函数本质上 是调用了Modifier这个接口的then方法 3.这个then方法的参数 是Background这对象
我们首先来看 Modifier这个接口的then 方法是个啥
可以看出来这个then方法本质上就是 A.then(B)
如果这个B是Modifier这个伴生对象 那么就返回A 如果不是 则 把AB 组合起来
注意这个是Modifier这个接口的 then方法实现, 但是回到我们小节开头的方法调用
Modifier.background(Color.Green)
这里本质是啥? 是用的Modifier这个伴生对象,所以 这里要看的是Modifier这个伴生对象的then方法实现
那他的实现就是
那么显然 Modifer.background 这个调用 就等于 Modifier then (Background) 同时他的返回值也是 Background这个对象
这里一定要搞清楚 不然后面是无法理解Modifer链式调用的
同时大家也能猜到了 这个Background也一定实现了Modifier这个接口
同样
Modifier.background(Color.Green).then(Modifier.padding(10.dp))
看上面这个代码, 等价于
CombinedModifier(Modifier.background(Color.Green),Modifier.padding(10.dp))
为什么?
因为Modifier.background 返回的是一个Background的Modifier,他的then方法就是接口Modifer里面的then方法 最终会走到 CombinedModifier 方法
到这里,可以下一个结论,在整个Compose的体系中, Modifier 只有三种
- CombinedModifier ,他的作用就是可以装载n个Modifier 放到一起
- Modifier接口内定义的名为Modifier的伴生对象
- 实现了Element这个接口的 Modifier
ComposedModifier
这是一个特殊的Modifier,专门用一个小节来理解一下 可以看下这个定义,一个private的class 我们无法直接调用
看下用法的效果区别:
// 这个写法代表 代码执行到这里 就立即创建一个padding效果的Modifier
Modifier.padding(10.dp)
// 这个写法代表 传进去了一个lambda表达式,等到真正需要用到的时候才会创建这个padding效果的Modifier
Modifier.composed { Modifier.padding(10.dp) }
有人要问了,真正需要用到的时候才创建是啥意思?,可以近似的理解为layout的时候才创建
可以跟一下代码
代码整体上并不难分析,最后就是落到 materialize 这个函数里面
可以看到里面其实就是一个循环,在循环的时候判断 如果是ComposedModifier 那就创建出来一个真正的Modifier对象
到这里是不是又懵了,这样做到底有啥特殊作用呢? 我直接在Box创建的时候就直接弄一个Modifier出来不行吗
比如说下面这2个写法,本质上 除了创建modifer的时机不同,其他在展示效果上一摸一样的
可以看一下下面这个例子
Column() {
val modifier1 = Modifier.composed {
var size by remember {
mutableStateOf(100.dp)
}
Modifier
.size(size)
.background(Color.Red)
.clickable {
size = 5.dp
}
}
var size by remember {
mutableStateOf(100.dp)
}
val modifier2 = Modifier
.size(size)
.background(Color.Blue)
.clickable {
size = 5.dp
}
Box(modifier1)
Box(modifier1)
Box(modifier2)
Box(modifier2)
}
代码很简单,其实就是4个box, 2个红色的box 用的是ComposdModifier的写法,2个蓝色的Box用的是传统的写法,点击事件都是 点击了以后size变小, 那么这两种写法 在这种场景下就有本质的区别了。
首先看红色的box,因为他们用的是ComposdModifier,在layout的时候才会进行调用,每次调用 都会创建出不同的Modifier对象,所以当你点击其中一个红Box的时候,只有那个被点击的红Box才会尺寸变小,另外一个是不会变小的,
而蓝色的box 则不同,因为你是在创建Column的时候就创建好的Modifer,2个蓝色的box本质上用的还是一个Modifer对象,所以这2个蓝色的Box 你随便点其中的一个,这2个会同时变小
有了这个区别,我们就很容易可以得到这个ComposedModifier的主要应用场景: 当你需要对外提供一个带状态的Modifier的时候,就可以考虑 使用ComposedModifier
例如:
// 对外暴露 一个公共的Modifer,而且这个Modifier是带状态的
//
fun Modifier.sizeChange() = composed {
var size by remember {
mutableStateOf(100.dp)
}
//LaunchedEffect(key1 = , block = )
//LocalContext
Modifier
.size(size)
.background(Color.Red)
.clickable {
size = 5.dp
}
}