赞
踩
组合:执行Composable函数体,生成LayoutNode视图树
布局:该阶段包含两个步骤:测量和放置。对于布局树中的每个节点LayoutNode,布局元素都会根据 2D 坐标来测量宽高尺寸并放置自己及其所有子元素。
绘制:将所有的LayoutNode实际绘制到屏幕上
组合阶段主要是生成并维护LayoutNode视图树,当我门在Activity中使用setContent时,会开始首次组合,此时会执行代码块中设计的所有Composable函数体,生成与之对应的LayoutNode视图树(具体过程可见《Android Jetpack Compose基础之Compose视图结构》)
在Compose中如果某个Compsable依赖了某个可变状态,该状态发生更新时,会触发当前Composable重新进行组合阶段,即重组,具体重组内容详见《Android Jetpack Compose基础之生命周期-重组》
在Compose中,每个LayoutNode都会根据来自父LayoutNode的布局约束来进行自我测量(类似传统View中的MeasureSpec)。布局约束中包含了父LayoutNode允许子LayoutNode的最大宽高和最小宽高,当父LayoutNode希望子LayoutNode测量的宽高为具体值时,约束中宽高的最大值和最小值是一致的
注意:LayoutNode不允许被多次测量。
步骤:
测量子节点:节点会测量其子节点(如果存在)
确定自己的大小:节点根据这些测量结果来决定自己的大小。
放置子节点:每个子节点根据节点自身的位置进行放置。
作用是用来修饰LayoutNode的宽高与原有内容在新宽高下摆放的位置,其具体调用调用方法如下
fun Modifier.layout( measure: MeasureScope.(Measurable, Constraints) -> MeasureResult ) = this then LayoutElement(measure) private data class LayoutElement( val measure: MeasureScope.(Measurable, Constraints) -> MeasureResult ) : ModifierNodeElement<LayoutModifierImpl>() { override fun create() = LayoutModifierImpl(measure) override fun update(node: LayoutModifierImpl) { node.measureBlock = measure } override fun InspectorInfo.inspectableProperties() { name = "layout" properties["measure"] = measure } interface Measurable : IntrinsicMeasurable { /** * Measures the layout with [constraints], returning a [Placeable] layout that has its new * size. A [Measurable] can only be measured once inside a layout pass. */ fun measure(constraints: Constraints): Placeable }
Measurable:表示被修饰的LayoutNode的测量句柄,通过内部的measure方法完成LayoutNode的测量。
constraints:表示来自父LayoutNode的布局约束
使用LayoutModifier实现Text顶部到文本基线的高度
Column(modifier = Modifier .fillMaxSize() .verticalScroll(scrollState) ) { Text( text = "Text Sample", modifier = Modifier .background(Color.Cyan) .firstBaselineToTop(40.dp) ) } fun Modifier.firstBaselineToTop(top: Dp) = Modifier.layout { measurable, constraints -> //将父LayoutNode的布局约束,直接传入Measure中,直接提供给被修饰的LayoutNode进行测量,测量的结果包装在Placeable示例中进行返回 val placeable = measurable.measure(constraints) //确认组件时存在内容基线的 check(placeable[FirstBaseline] != AlignmentLine.Unspecified) //获取基线高度 val firstBaseline = placeable[FirstBaseline] //应摆放的顶部高度=所设置的顶部到基线的高度-实际组件内容顶部到基线的高度 val placeableY = top.roundToPx() - firstBaseline //该组件占有的高度=应摆放的顶部高度+实际内容的高度 val height = placeable.height + placeableY layout(placeable.width, height) { //指定原有应该绘制的内容在新的高宽下摆放的相对位置 placeable.placeRelative(0, placeableY) } }
LayoutModifier可以类比于定制具体View,如果需要定制ViewGoup,就需要使用LayoutCompsable了,它的源码如下
@UiComposable @Composable inline fun Layout( content: @Composable @UiComposable () -> Unit,//我们声明的子组件信息 modifier: Modifier = Modifier,//外部传入的修饰符 measurePolicy: MeasurePolicy//表示测量策略, ) { val compositeKeyHash = currentCompositeKeyHash val localMap = currentComposer.currentCompositionLocalMap ReusableComposeNode<ComposeUiNode, Applier<Any>>( factory = ComposeUiNode.Constructor, update = { set(measurePolicy, SetMeasurePolicy) set(localMap, SetResolvedCompositionLocals) @OptIn(ExperimentalComposeUiApi::class) set(compositeKeyHash, SetCompositeKeyHash) }, skippableUpdate = materializerOf(modifier), content = content ) }
粗略仿照column,它本身的源码如下
@Composable
inline fun Column(
modifier: Modifier = Modifier,
verticalArrangement: Arrangement.Vertical = Arrangement.Top,
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
content: @Composable ColumnScope.() -> Unit
) {
val measurePolicy = columnMeasurePolicy(verticalArrangement, horizontalAlignment)
Layout(
content = { ColumnScopeInstance.content() },
measurePolicy = measurePolicy,
modifier = modifier
)
}
自定义CustomColumnLayout主要是为了直观看出测量高宽的过程,运行效果自行脑补,编码过程如下。
CustomColumnLayout { Text(text = "CustomColumnLayout", modifier = Modifier.background(Color.Cyan)) Text(text = "CustomColumnLayout2", modifier = Modifier.background(Color.Cyan)) } @Composable fun CustomColumnLayout( modifier: Modifier = Modifier, // 此处可添加自定义的参数 content: @Composable () -> Unit ) { Layout( modifier = modifier, content = content ) { measurable, constraints -> val placeables = measurable.map { measurable -> //测量每个子组件 measurable.measure(constraints) } var yPosition = 0 layout(constraints.maxWidth, 2000) { placeables.forEach { placeable -> placeable.placeRelative(x = 0, y = yPosition) yPosition += placeable.height } } } }
绘制就是将所有的LayoutNode实际绘制到屏幕之上咯
@Composable
fun Canvas(modifier: Modifier, onDraw: DrawScope.() -> Unit) =
Spacer(modifier.drawBehind(onDraw))
@Composable
@NonRestartableComposable
fun Spacer(modifier: Modifier) {
Layout(measurePolicy = SpacerMeasurePolicy, modifier = modifier)
}
DrawScope 作用域中,compose提供了基础的绘制API
drawLine:绘制线
drawRect:绘制矩形
drawImage:绘制图片
drawRoundRect:绘制圆觉矩形
drawCircle:绘制圆
drawOval:绘制椭圆
drawArc:绘制弧线
drawPath:绘制路径
drawPoints:绘制点
可以允许在绘制时自定义绘制层级Z轴的层级
,方法需要传入ContentDrawScope作用域lambda,而它ContentDrawScope是继承自DrawScope,最终通过drawContent()方法绘制组件本身的内容
fun Modifier.drawWithContent( onDraw: ContentDrawScope.() -> Unit ): Modifier = this then DrawWithContentElement(onDraw) interface ContentDrawScope : DrawScope { /** * Causes child drawing operations to run during the `onPaint` lambda. */ fun drawContent() } private data class DrawWithContentElement( val onDraw: ContentDrawScope.() -> Unit ) : ModifierNodeElement<DrawWithContentModifier>() { override fun create() = DrawWithContentModifier(onDraw) override fun update(node: DrawWithContentModifier) { node.onDraw = onDraw } override fun InspectorInfo.inspectableProperties() { name = "drawWithContent" properties["onDraw"] = onDraw } }
绘制如下效果
@Composable ![请添加图片描述](https://img-blog.csdnimg.cn/direct/9f19af3b2c77460099ad488e7c03dbd5.png) fun BaseDrawWithContent() { var pointerOffset by remember { mutableStateOf(Offset(0f, 0f)) } Column(modifier = Modifier .fillMaxWidth() .height(200.dp) .pointerInput(key1 = "dragging") { detectDragGestures { change, dragAmount -> pointerOffset += dragAmount } } .onSizeChanged { pointerOffset = Offset(it.width / 2f, it.height / 2f) } .drawWithContent { drawContent() drawRect( Brush.radialGradient(listOf(Color.Transparent, Color.Black), center = pointerOffset, radius = 100.dp.toPx()) ) } ) { Text( text = "drawWithContent", modifier = Modifier .fillMaxSize() .background(color = Color.Cyan) ) } }
在绘制组件拓展的内容,在绘制组件本身,用作自定义组件背景
定制的绘制逻辑onDraw最终被传入DrawBackgroundModifier的主构造函数中,在draw()方法中先绘制自定义的内容onDraw,然后在绘制组件内容drawContent
fun Modifier.drawBehind( onDraw: DrawScope.() -> Unit ) = this then DrawBehindElement(onDraw) private data class DrawBehindElement( val onDraw: DrawScope.() -> Unit ) : ModifierNodeElement<DrawBackgroundModifier>() { override fun create() = DrawBackgroundModifier(onDraw) override fun update(node: DrawBackgroundModifier) { node.onDraw = onDraw } override fun InspectorInfo.inspectableProperties() { name = "drawBehind" properties["onDraw"] = onDraw } } internal class DrawBackgroundModifier( var onDraw: DrawScope.() -> Unit ) : Modifier.Node(), DrawModifierNode { override fun ContentDrawScope.draw() { onDraw() drawContent() } }
给文本绘制一个bg
@Composable
fun BaseDrawBehind() {
Text(text = "DrawBehind", modifier = Modifier.drawBehind {
drawRoundRect(Color.DarkGray, cornerRadius = CornerRadius(10.dp.toPx()))
})
}
背景:在drawScop中绘制时,绘制一些有关对象时,如ImageBitmap,Paint、Path时,当组件发生重绘时,由于drawScop会反复执行,会使其中声明的对象发生频繁创建,
作用:drawWithCache会缓存在其中创建的对象。只要绘制区域的大小不变,或者读取的任何状态对象都未发生变化,对象就会被缓存。此修饰符有助于改进绘制调用的性能,因为它不必对绘制时创建的对象(例如:Brush, Shader, Path 等)进行重新分配。
注意:请仅在创建必须缓存的对象时才使用 Modifier.drawWithCache。如果在无需缓存对象时使用此修饰符,可能会导致不必要的 lambda 分配。
方法需要传入CacheDrawScope作用域的lambda并返回DrawResult
fun Modifier.drawWithCache( onBuildDrawCache: CacheDrawScope.() -> DrawResult ) = this then DrawWithCacheElement(onBuildDrawCache) private data class DrawWithCacheElement( val onBuildDrawCache: CacheDrawScope.() -> DrawResult ) : ModifierNodeElement<CacheDrawModifierNodeImpl>() { override fun create(): CacheDrawModifierNodeImpl { return CacheDrawModifierNodeImpl(CacheDrawScope(), onBuildDrawCache) } override fun update(node: CacheDrawModifierNodeImpl) { node.block = onBuildDrawCache } override fun InspectorInfo.inspectableProperties() { name = "drawWithCache" properties["onBuildDrawCache"] = onBuildDrawCache } } fun CacheDrawModifierNode( onBuildDrawCache: CacheDrawScope.() -> DrawResult ): CacheDrawModifierNode { return CacheDrawModifierNodeImpl(CacheDrawScope(), onBuildDrawCache) }
@Composable
fun BaseDrawWithCache() {
Text(text = "drawWithCache", modifier = Modifier.drawWithCache {
val brush = Brush.linearGradient(listOf(Color.Red, Color.Green))
onDrawBehind {
drawRoundRect(brush, cornerRadius = CornerRadius(10.dp.toPx()))
}
})
}
可实现对图层的缩放、平移、旋转、裁剪、透明度等功能
使用方式
@Composable fun BaseGraphicsLayer() { var progressX by remember { mutableStateOf(0.1f) } Slider(value = progressX, onValueChange = { progressX = it }, valueRange = 0f..2f) var progressY by remember { mutableStateOf(0.1f) } Slider(value = progressY, onValueChange = { progressY = it }, valueRange = 0f..2f) Image( painter = painterResource(id = R.mipmap.btn_shara), contentDescription = "", modifier = Modifier.graphicsLayer( scaleX = progressX, scaleY = progressY ) ) }
————note end————
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。