初识DrawModifier
先看下面这段代码
Box(
modifier = Modifier
.requiredSize(50.dp)
.background(Color.Yellow)
.requiredSize(10.dp)
)
应该知道这个黄色的方块,终究的巨细应该是10dp,为啥? 由于background 一直都是会用到他右边的尺寸巨细
能够看下这个background 实际上便是一个DrawModifier
看一下 下面的写法:
Box(
modifier = Modifier
.background(Color.Red)
.size(50.dp).drawWithContent {
}
)
跑一下,能看出来 这是一个50dp巨细的赤色矩形
可是我假如略微改一下代码,变成下面这样的:
Box(
modifier = Modifier
.size(50.dp).drawWithContent {
}.background(Color.Red)
)
你就会发现这个赤色的矩形消失了
再改一下:
Box(
modifier = Modifier
.size(50.dp).drawWithContent {
drawContent()
}.background(Color.Red)
)
你会发现 这个赤色矩形又回来了,为什么drawContent办法不调用,结果就会犯错呢?
带着上述的疑问, 咱们跟一下DrawModifier的代码,看看 他在compose中的制作流程中,起到了一些什么作用,为什么会造成上述比方中的现象
注意这篇文章需求 LayoutModifier 作为基础
源码分析
看下DrawModifer 是在什么时分影响布局的吧
这个toWrap,上一节咱们介绍过,本质上便是一层层嵌套的ModifierLayoutNode,是一个LayoutNodeWrapper的目标
再看下这个entities 是啥
这个LayoutNodeEntitiy 里边还包裹了一个Modifier,
能够看下他有几个子类
这个就很要害了,咱们再切回去看看
首要这个DrawEntity便是咱们方才介绍过的,LayoutNodeEntitiy的子类,他包裹了咱们的modifer,
看下第二个参数
这儿能够知道 ,这第二个参数其实固定是0,虽然你看起来他是一个变量,但他本质上仍是一个0
再看下这个add函数,一个简略的链表操作
咱们首要看一下这个entities是啥
能够看出来,这儿 entities 便是一个长度为6的数组,
而这个数组里边 每一个方位上的元素都是固定的比方方位0 上放的永久便是drawmodifier,有人要问 假如有多个drawmodifer怎样办, 这儿的数据结构其实和java中的hashmap非常向,这个数组中的每个元素 本质上便是一个链表,
举个比方,假设有个3个drawmodifer 组成了这个链表 LayoutNodeEntity(drawmodifer3)->LayoutNodeEntity(drawmodifer2)>LayoutNodeEntity(drawmodifer1)
然后这个链表就放到了 这个enties的 第0个方位上,仅此而已
搞清楚这个数据结构 其他地方就不难理解了,
下面就持续看下 draw办法 里边干了啥,是如何被DrawModifier影响的
首要看下draw办法 里边的layer,这儿你就理解为图层的意思就能够了, 能够把一个视图分成几个独立的layer,这些layer 叠加以后展现出来的 便是你看到的view的作用
比方这儿的alpha,底层也便是用的layer
接着看,其实下面这个姓名很长的draw函数 在有了前面的分析基础上,就很好理解了,便是去entities这个数组里边 取drawmodifer方位上的链表, 然后看一下这个链表有没有值,有值就 把内容和drawmodifier一起制作 没有值,那就只制作内容
咱们首要看下这个performDraw函数,这个函数是会在 没有drawModifier时分被调用,这儿的wrapped 是啥? 前面LayoutModifier的文章里介绍过的,A 包裹B,这个B 便是wrapped, 那为什么 这儿wrapped还有个判空的问号? 由于最里层的Node节点 明显是没有包裹任何东西的
并且这个咱们看下这个performDraw函数办法里边 终究仍是咱们调用的咱们前面介绍的draw办法,你能够理解为一个递归的调用逻辑
这儿还应该要注意的是,在compose的体系中,所有组件底层都是用drawmodifier来完成的,比方text组件,image组件,等等
咱们接着看,当有drawmodifier的时分 制作的流程有什么变化
看上面的调用链 咱们即可知道,制作的进程其实便是在drawScope.draw 这个办法传递的 lambda,也便是那个block
这个lambda里边 最重要的便是当时这个modifier的draw办法了
这个draw办法是啥?其实便是那个接口啊
换个写法:
其实便是调用的咱们写的drawWithContent这个函数的回来值了
这儿有人会问,这个drawConent办法不调用的话,界面就展现不出来 这是为啥?
看到这你应该能理解了,由于drawmodifier的调用 本质上是一个drawmodifier的链表的顺序调用,
而完成这个顺序调用的便是这个drawContent办法, 你假如不去调用这个drawContent办法 那就意味着
你这个组件的制作 只制作了你的head node部分,而剩余的部分你都没有制作。。。
再来具体看一下这个drawContent办法
这儿便是判别一下 假如当时节点的drawModifer链表上还有节点 那持续制作当时节点的drawmodifer 否则就去你包裹的子节点的drawModifer链表上去制作
这么说如同有点绕了,咱们换个说法
咱们能够考虑一下 假如咱们的界面是如下的结构
A包裹B包裹C,且每个节点都有自己的DrawModifier链表,那他实际上的制作进程就能够归纳为
注意了, 这儿并不意味着, a1的内容一定会覆盖掉a2的内容,由于实际上 谁是谁的爹 这件事 是能够调整的
怎样理解这句话?
看下面这个比方
一个是先制作content 再制作一个圆形,那明显便是这个圆点会遮盖住content
反之,则content会遮盖住这个圆点
一些比方分析
Box(
modifier = Modifier.background(Color.Red).background(Color.Blue).size(40.dp)
)
这个Box 会显现什么色彩? 明显是蓝色, 由于有了第二小节的分析 咱们知道,
先制作了赤色背景的drawmodifer,然后再制作了蓝色背景的Modifer, 左边包裹着右边 蓝色天然就覆盖了赤色,所以终究是蓝色
再看下这个比方:
Box(
modifier = Modifier.requiredSize(80.dp).background(Color.Blue).requiredSize(10.dp)
)
这个蓝色的方块终究有多大?,答案是10dp ,为啥?
从右往左遍历的时分 DrawModifer是和toWrap来绑定在一起的, 到这个比方中便是 遍历到了 蓝色方块的DrawModifier的时分 他就和 toWrap绑定在一起了,这个toWrap 明显便是那个10dp巨细的layoutModifer
简而言之, drawModifier的制作巨细总是跟从着 他右边的layoutModifier
再看最后一个比方
Box(
modifier = Modifier.size(40.dp).padding(19.dp).background(Color.Blue)
)
这儿蓝色的方块有多大? 应该是一个size为2的 方块对吧,
可是你要如何得出这个size为2的定论?
前面介绍过 drawModifer也便是这个比方中的background 的巨细是跟从他右边的layoutModifer的
可是这个比方中右边没有了? 既然右边没有了 为啥终究巨细是2
回忆上一个文章中layoutmodifer的常识,这儿最外层是一个40dp的size,然后包裹着一个padding为19的layout,天然剩余的空间就只有40-19*2=2了, 所以终究的blue 的方块巨细便是2