Jetpack Compose - 初识Modifier (十一)

1,315 阅读5分钟

modifier: Modifier = Modifier 的用法

Google 推荐在自定义一个Compose的时候,第一个有默认参数值的类型 要用 modifer ,例如:

image.png

有人可能会问为啥要这样推荐呢,因为第一个有默认参数类型的 参数 在实际调用的时候 可以不写 xxx=yyy

就是让调用者方便一点 仅此而已, 接着看上面红框中的代码 ,有人觉得 这个代码好奇怪啊

因为我点进去以后 发现这个Modifier是一个对象啊,对象也可以做参数类型了? image.png

仔细看看,其实不然

image.png

这个Modifier本质上 他首先是一个接口,在这个接口之中,我们定义了一个

companion object : Modifier

Modifier的伴生对象,注意这个对象是实现了Modifier接口的,只是这个对象名字和interface的名字一样。

这个语法糖在很多地方都有调用,比如kotlin的协程中 就有这个东西,有兴趣的可以自己看一下,

所以再看一下这个写法:

image.png

第一个参数是接口,默认值是一个名为Modifier的半生对象 他实现了Modifer 这个接口

如何理解Modifier的链式调用

看下面这行代码

Modifier.background(Color.Green)

我们点进去看以后可以得到下图:

image.png

通过这张图,我们可以得到如下若干个信息

1.backgroun是Mofifer接口的一个扩展函数 2.这个扩展函数本质上 是调用了Modifier这个接口的then方法 3.这个then方法的参数 是Background这对象

我们首先来看 Modifier这个接口的then 方法是个啥

image.png

可以看出来这个then方法本质上就是 A.then(B)

如果这个B是Modifier这个伴生对象 那么就返回A 如果不是 则 把AB 组合起来

注意这个是Modifier这个接口的 then方法实现, 但是回到我们小节开头的方法调用

Modifier.background(Color.Green)

这里本质是啥? 是用的Modifier这个伴生对象,所以 这里要看的是Modifier这个伴生对象的then方法实现

那他的实现就是

image.png

那么显然 Modifer.background 这个调用 就等于 Modifier then (Background) 同时他的返回值也是 Background这个对象

这里一定要搞清楚 不然后面是无法理解Modifer链式调用的

同时大家也能猜到了 这个Background也一定实现了Modifier这个接口 image.png image.png image.png

同样

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 只有三种

  1. CombinedModifier ,他的作用就是可以装载n个Modifier 放到一起
  2. Modifier接口内定义的名为Modifier的伴生对象
  3. 实现了Element这个接口的 Modifier

ComposedModifier

这是一个特殊的Modifier,专门用一个小节来理解一下 可以看下这个定义,一个private的class 我们无法直接调用

image.png

看下用法的效果区别:

// 这个写法代表 代码执行到这里 就立即创建一个padding效果的Modifier
Modifier.padding(10.dp)
// 这个写法代表 传进去了一个lambda表达式,等到真正需要用到的时候才会创建这个padding效果的Modifier
Modifier.composed { Modifier.padding(10.dp) }

有人要问了,真正需要用到的时候才创建是啥意思?,可以近似的理解为layout的时候才创建

可以跟一下代码

image.png

image.png

image.png

image.png

image.png

代码整体上并不难分析,最后就是落到 materialize 这个函数里面

可以看到里面其实就是一个循环,在循环的时候判断 如果是ComposedModifier 那就创建出来一个真正的Modifier对象

到这里是不是又懵了,这样做到底有啥特殊作用呢? 我直接在Box创建的时候就直接弄一个Modifier出来不行吗

比如说下面这2个写法,本质上 除了创建modifer的时机不同,其他在展示效果上一摸一样的 image.png

可以看一下下面这个例子

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
        }
}