“This is the second day of my participation in the First Challenge 2022, for more details: First Challenge 2022”.
In the last article, we went into Jetpack Compose — Layout Principles and Custom Layout (I) — Digging (juejin. Cn), we looked at the Layout process and briefly implemented two custom layouts. Let’s turn our attention to Modifier and intrinsic property measurement
Deep Dive into Jetpack Compose layouts
Modifier
nature
On the nature of the Modifier, RugerMc big man in the diagram of the Modifier implementation principle, should be so simple in this article has been explained very clearly, I will not gild the snake. However, for the convenience of subsequent writing, I will simply say a few points here:
- Modifier is an interface that contains three direct implementation classes or interfaces:
Associated object Modifier
, internal subinterfacesModifier.Element
andCombinedModifier
. Associated object Modifier
Is the daily use of the most, the latter two are internal implementation, the actual development need not pay attention toModifier.xxx()
The method actually creates oneModifier
An instance of the implementation class of the interface. Such asModifier.size()
Will createSizeModifer
The instance
@Stable
fun Modifier.size(size: Dp) = this.then(
SizeModifier(
/* omit the details */))Copy the code
-
XXX ().yyy().zzz() actually creates a Modifier chain in the internal order of XXX -> YYY -> ZZZ, connected by CombinedModifier. The Modifie interface provides foldIn/foldOut methods that allow us to iterate through each Modifier in order/reverse order
Here’s an illustration from the article above:
We can just go through it very quickly. Such as:
@Composable
fun TraverseModifier(a) {
val modifier = Modifier
.size(40.dp)
.background(Color.Gray)
.clip(CircleShape)
LaunchedEffect(modifier){
// Iterate through the Modifier sequentially
modifier.foldIn(0){ index , element : Modifier.Element ->
Log.d(TAG, "$index -> $element")
index + 1}}}Copy the code
Its output is:
0 -> androidx.compose.foundation.layout.SizeModifier@78000000 1 -> Background(color=Color(...) , brush = null, alpha = 1.0, shape = RectangleShape) 2 - > SimpleGraphicsLayerModifier (...).Copy the code
role
Next, let’s see how the Modifier works in the layout
Let’s start with an example
@Composable
fun ModifierSample1(a) {
/ / the parent element
Box(modifier = Modifier
.width(200.dp)
.height(300.dp)
.background(Color.Yellow)){
/ / child elements
Box(modifier = Modifier
.fillMaxSize()
.wrapContentSize(align = Alignment.Center)
.size(50.dp)
.background(Color.Blue))
}
}
Copy the code
It actually looks like this
(The rounded corner in the upper left corner is the edge of the screen)
Let’s take a step-by-step look at how this actually happens. Here we choose the child element, the smaller blue Box, to look at its measure and place procedures.
First, measure. The parent element specifies that it is 200 x 300, which is the maximum space that the child element can take up. so
-
Initial: Initial constraint W :0-200, h:0-300
-
FillMaxSize () : occupies the maximum space. The min value of the constraint is changed to be the same as Max, that is, W :200-200, h:300-300
-
WrapContentSize () : The constraint min is changed back to 0, w:0-200, h:0-300, to accommodate the content size
-
Size () : Specifies the exact size. The constraints become W :50-50, H :50-50
-
Background () : Has no effect on the size constraint
Finally, under the Modifier, the Box receives a W :50-50, H :50-50 constraint. The states that have gone here are as follows:
Next, the Layout widget inside the Box executes the measure method to get its own size: 50 by 50. This size is reversely passed back to the last item in the Modifier chain and the place begins. The following:
- Background () : Omitted here
- Size (50.dp) : Measure your own size: 50*50 and create your own position instructions accordingly
- WrapContentSize () : Measure its own size: 200*300, and know its child size: 50*50, and center it. Create your own location directive from this.
- FillMaxSize () : resolves its own size and position
This process is similar to Layout widgets, except that each Modifier has only one child (that is, the next element in the Modifier chain). In fact, if you look at the code, you can easily see the similarities
Take the code for the wrapContentSize modifier, which implements the Measure method of the WrapContentModifier class as follows
fun MeasureScope.measure(
measurable: Measurable,
constraints: Constraints
): MeasureResult {
// Set constraints
val wrappedConstraints = Constraints(/ * * /)
// Measure the placement item
val placeable = measurable.measure(wrappedConstraints)
val wrapperWidth = placeable.width.coerceIn(constraints.minWidth, constraints.maxWidth)
val wrapperHeight = placeable.height.coerceIn(constraints.minHeight, constraints.maxHeight)
The // layout function is placed in the specified position and returns the result
return layout(
wrapperWidth,
wrapperHeight
) {
val position = alignmentCallback(
IntSize(wrapperWidth - placeable.width, wrapperHeight - placeable.height),
layoutDirection
)
placeable.place(position)
}
}
Copy the code
How about that? Is it similar?
subsequent
On the Modifier we first look at these. In the next article, we’ll touch on the inherent properties to measure this property and improve on the vertical layout we implemented in the first article.
All code for this article can be found here