This is the first day of my participation in the August Challenge. For details, see:August is more challenging
This learning note is mainly based on the official document to learn the layout of Android Compose. By learning this note, you can roughly understand the three layouts of Row,Column and Box in Compose, and also learn how to set the background color, size and other properties of the layout. Basic knowledge (Compose layout | Jetpack Compose | Android Developers) click on the link to view the official document directly, enter text below:
An overview of the
This study note is mainly about the layout in Compose. The main content is from the official document. You can directly click the link above to access the official document.
The target
The layout system in Compose serves two purposes:
-
Achieve high performance. (In a typical Android View system, you should always try to avoid multiple layers of layout nesting, which can cause the layout to be drawn multiple times, affecting performance, but the way layouts are measured in Compose is called intrinsic property measurement, which achieves high performance by avoiding multiple measurements of layout children.) This is the basic concept, but we’ll learn how to do it later.
-
Makes it easy for developers to write custom layouts. (In the common Android View system, it is tedious to define the layout. On the one hand, we should consider the process of measure, on the other hand, we should consider the process of layout.) It’s the same basic concept that needs to be compared later.
Combinable function
Composable functions are the basic building blocks of Compose, returning functions for Unit that describe parts of the interface. Such a function can take some input and then generate the information displayed on the screen based on the input.
A composable function may emit multiple interface elements, and if we do not specify a proper layout for these elements, the final appearance may not be what we want. The following code demonstrates that emitting two texts in a composable function may not look like what we want:
@Composable
private fun noLayout(){
Text(text = "the first text")
Text(text = "the second test")
}
Copy the code
The combinable function above does not provide a layout for how to arrange the two elements, so the end result is that the two texts will be stacked together, similar to FrameLayout:
Vertical arrangement of elements (Column
)
Columns allow you to place multiple elements vertically on the screen, similar to the effect of Oritation :vertical in LinearLayout.
@Composable
private fun columnLayout(){
Column() {
Text(text = "the first text",fontSize = 20.sp,color = Color.Black)
Text(text = "the second test",fontSize = 16.sp,color = Color.Red)
}
}
Copy the code
The above code wraps the columns around the two texts. Here’s what happens when you use columns:
Horizontal arrangement of elements (Row
)
Use Row to place its children horizontally on the screen, similar to the effect of oritation:horitation in LinearLayout:
@Composable
private fun rowLayout(){
Row() {
Text(text = "the first text",fontSize = 20.sp,color = Color.Black)
Text(text = "the second test",fontSize = 16.sp,color = Color.Red)
}
}
Copy the code
The effect is as follows:
Layering elements (Box
)
Use Box to place one element on top of another, as follows:
@Composable
private fun boxLayout(a){
Box() {
Text(text = "the first text",fontSize =30.sp,color = Color.Black)
Text(text = "the second test",fontSize = 16.sp,color = Color.Red)
}
}
Copy the code
The effect is as follows:
While the above effect looks similar to the one you would have done without using any, using Box allows you to specify some additional parameters that you would not have achieved without using any layout. The following code specifies the alignment of the subitems:
@Composable
private fun boxLayout(a){
Box(contentAlignment = Alignment.Center) {
Text(text = "the first text",fontSize = 30.sp,color = Color.Black)
Text(text = "the second test",fontSize = 12.sp,color = Color.Red)
}
}
Copy the code
The effect is as follows:
In general, the three building blocks above can be used to meet the requirements, which can be achieved by combining these layouts with combinable functions, and since there is no need to worry about multiple rendering, we can combine these layouts with little concern for performance.
Improve the parameters
We have shown that we can set parameters in Box to change the position of the subitems. We can also specify parameters in Column and Row to achieve different effects. By specifying the horizontalArrangement and verticalAlignment parameters (for a Row) or the verticalArrangement and horizontalAlignment parameters (for a Column), You can specify the location of its children.
- The following code specifies
Column
The effect of the middle term being vertically at the bottom and horizontally at the middle:
@Composable
private fun columnLayoutChild(a){
Column(
verticalArrangement = Arrangement.Bottom,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxSize()
) {
Text(text = "the first text",fontSize = 30.sp,color = Color.Black)
Text(text = "the second test",fontSize = 12.sp,color = Color.Red)
}
}
Copy the code
Since columns by default resize themselves according to the size of their children, the modifer argument is used to specify that the size of a Column is full of its parent container, otherwise verticalArrangement = arrangement. Bottom has no effect.
- The following code specifies
Row
The position of the subitem:
@Composable
private fun rowLayoutChild(a){
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End,
modifier = Modifier.fillMaxSize()
) {
Text(text = "the first text",fontSize = 30.sp,color = Color.Black)
Text(text = "the second test",fontSize = 12.sp,color = Color.Red)
}
}
Copy the code
In the above code, we first specify the size of the Row to fill its parent container, and then specify that the children of the Row should be right-most horizontally and centered vertically. The effect is as follows:
- Here’s what we’re familiar with
Row
andColumn
The specifiedweight
Property, as shown below:
@Composable
private fun columnWeightChild(a) {
Column(modifier = Modifier.fillMaxSize()) {
Text(
text = "the first text",
fontSize = 30.sp,
color = Color.Black,
modifier = Modifier.weight(1f)
.background(color = Color.LightGray)
)
Text(
text = "the second test",
fontSize = 12.sp,
color = Color.Red,
modifier = Modifier.weight(1f)
.background(color = Color.DarkGray)
)
}
}
Copy the code
The effect is as follows:
The modifier
In the argument section above, we’ve seen some attributes, just like we define attributes in an XML layout. We need to add more effects, so we need more attributes. Now we’ll learn more about the attributes that the system provides for us, also called modifiers.
Modifiers are standard Kotlin objects that can be created by calling an Modifier class function, or by connecting multiple Modifier functions to form a combination effect.
size
Specify the size
We’ve already used the size modifier to specify the size of an item, as shown below:
@Composable
private fun sizeColumn(a){
Column(
modifier = Modifier
.size(width = 200.dp, height = 200.dp)
.background(color = Color.Red),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = Width and height: 200dp,
color = Color.White
)
}
}
Copy the code
In the code above, we give the Column a size, width, and height of 200dp, and we also give it a color so you can see the effect. You can see that the size and background attributes are Modifier class functions, the two functions can be combined to achieve the effect we want. Here is the result:
The above illustrates specifying the size of a Column. By default, children should conform to the parent constraint, that is, the size of children cannot exceed the size of the parent. By default, if a child exceeds the size of its parent, it does not take effect, as shown below:
@Composable
private fun bigParentSize(a) {
Box(
modifier = Modifier
.size(300.dp, 600.dp)
.background(color = Color.LightGray),
contentAlignment = Alignment.Center
) {
Column(
modifier = Modifier
.size(200.dp)
.background(color = Color.DarkGray),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "The child is greater than the parent.",
color = Color.Red,
modifier = Modifier
.size(width = 50.dp, height = 300.dp)
.background(color = Color.Green)
)
}
}
}
Copy the code
In the above code, we specify that the Column is 200dp wide and 200DP high, and that the child Text is 300DP high, which is larger than the parent. In theory, we should not be able to see the content in the Text, or only see part of the content, because part of the content is above the parent item, but in fact, we can still see the complete content in the Text, because the size of the child is restricted by the parent item, and the height of the child itself cannot exceed the height of the parent item. The effect is as follows:
requiredSize
Break the parent’s size limit
If we simply don’t want the parent constraint and keep the child size, then we should use requiredSize() when setting the size. The size specified using this method is not subject to the parent constraint:
@Composable
private fun ignoreParentSize(a) {
Box(
modifier = Modifier
.size(300.dp, 600.dp)
.background(color = Color.LightGray),
contentAlignment = Alignment.Center
) {
Column(
modifier = Modifier
.size(200.dp)
.background(color = Color.Blue),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Bottom
) {
Text(
text = "The child is much larger than the parent.",
color = Color.White,
fontSize = 40.sp,
modifier = Modifier
.requiredSize(width = 80.dp, height = 500.dp)
.background(color = Color.Green)
)
}
}
}
Copy the code
In the above code, we still set the height of the child to be higher than the height of the parent, and to see the final effect, we set the Text size and set a larger parent Box outside. The final effect is as follows:
wrapContentSize
Set subitem alignment
In the above code, we set the size of the child item to be larger than the size of the parent item. By default, the text is displayed from top to bottom, so the top half of the text is invisible. What if we want to see the top half of the text? Instead, use the wrapContentSize property, as shown below:
@Composable
private fun ignoreParentSize(a) {
Box(
modifier = Modifier
.size(300.dp, 600.dp)
.background(color = Color.LightGray),
contentAlignment = Alignment.Center
) {
Column(
modifier = Modifier
.size(200.dp)
.background(color = Color.Blue),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Bottom
) {
Text(
text = "The child is much larger than the parent.",
color = Color.White,
fontSize = 40.sp,
modifier = Modifier
.requiredSize(width = 80.dp, height = 500.dp)
.background(color = Color.Green)
.wrapContentSize(align = Alignment.BottomCenter)
)
}
}
}
Copy the code
We use the same code as before, but we add the wrapContentSize property to the Text subitem, and specify align as bottom center, so that the top half of the Text is displayed as much as possible. The effect is as follows:
Note that the wrapContentSize property can only specify the alignment of the subitem itself. This is because there is less text in the subitem. If the bottom is centered, the upper part of the text will appear, but it will not.
As you can see, when there is more text, you can only see the text in the middle, not the text up and down.
Another note: You can use the align property of wrapContentSize to specify the alignment of the inner text when the number of text is small, but this does not take effect when the text exceeds the height of the subitem. As shown below, the following two images specify align = Alignment.Center:
In addition, how the text itself is aligned should be set using the textAlign property.
Alternatively, if you just want to set width or height, you can use methods like Width(), height(),requiredWidth(),requiredHeight(), and so on.
If we only need to fill the parent in width or height, we can use the fillMaxWidth(),fillMaxHeight(), fillMaxSize() methods, as shown below:
@Composable
private fun fillParent(a) {
Column() {
Box(
modifier = Modifier
.size(200.dp, 100.dp)
.background(color = Color.Red),
contentAlignment = Alignment.Center
) {
Text(
text = "Full width of parent", color = Color.White, modifier = Modifier
.fillMaxWidth()
.background(color = Color.Gray)
)
}
Spacer(modifier = Modifier.height(10.dp))
Box(
modifier = Modifier
.size(200.dp, 100.dp)
.background(color = Color.Yellow),
contentAlignment = Alignment.Center
) {
Text(
text = "Full width of parent",
color = Color.White,
modifier = Modifier
.fillMaxHeight()
.background(color = Color.Gray)
)
}
Spacer(modifier = Modifier.height(10.dp))
Box(
modifier = Modifier
.size(200.dp, 100.dp)
.background(color = Color.Blue),
contentAlignment = Alignment.Center
) {
Text(
text = "Full of the parent term.",
color = Color.White,
modifier = Modifier
.fillMaxSize()
.background(color = Color.Gray)
)
}
}
}
Copy the code
The running effect is as follows:
paddingFromBaseline
Set the distance based on the text baseline
For text, if you want to set the distance based on the text baseline, you can use paddingFromBaseline:
@Composable
private fun distanceBaseline(a) {
Row() {
Box (
modifier = Modifier.background(color = Color.Gray)
){
Text(
text = "Set distance based on text baseline",
modifier = Modifier
.paddingFromBaseline(
top = 100.dp,
bottom = 50.dp,
)
.background(color = Color.Red),
color = Color.White
)
}
Spacer(modifier = Modifier.width(20.dp))
Box (
modifier = Modifier.background(color = Color.Gray)
){
Text(
text = Sets the Padding "",
modifier = Modifier
.padding(
top = 100.dp,
bottom = 50.dp,
)
.background(color = Color.Red),
color = Color.White
)
}
}
}
Copy the code
offset
Set offset
To set the layout relative to its original position, set offset. The offset can be either positive or negative, as shown below:
@Composable
private fun paddingAndOffset(a){
Column {
Text(text = "This is a text.",
modifier = Modifier
.background(color = Color.Gray)
.offset(x = 10.dp)
)
Text(text = "This is a text.",
modifier = Modifier
.padding(start = 10.dp,top = 10.dp)
.background(color = Color.Red)
)
}
}
Copy the code
As you can see from the above picture, using offset does not change the measurement of the composable.
Type safety in Compose
In Compose, there are modifiers that apply only to certain composable items, such as the weight modifier you learned above that applies only to Row and Column.
Box
In thematchParentSize
When using a Box layout, if we want children to fill up the parent without affecting the size of the parent, we should use the matchParentSize modifier. This modifier only applies to the scope of the Box, meaning that the immediate children of the Box can use this modifier, as shown below:
@Composable
private fun matchParentSizeInBox(a){
Box(modifier = Modifier.background(color = Color.Gray)) {
Spacer(modifier = Modifier
.background(color = Color.Cyan)
.matchParentSize())
Text(text = "This is a big text box.",modifier = Modifier.size(200.dp)
.wrapContentSize(align = Alignment.Center),
textAlign = TextAlign.Center,
)
}
}
Copy the code
The above code runs as follows:
As you can see from the above, Box contains two children, Spacer and Text, with Spacer set to the same size as the parent and Text having its own size. The final size of Box is set to the size of the largest of all its children, and Spacer and size are the same as the size of the largest child (as you can see from the background color).
We learned earlier that fllMaxSize makes the child size the same as the parent, but the problem with fillMaxSize is that the child affects the parent size, as shown below:
@Composable
private fun fillMaxSizeInBox(a){
Box(modifier = Modifier.background(color = Color.Gray)) {
Spacer(modifier = Modifier
.fillMaxSize()
.background(color = Color.Cyan)
)
Text(text = "This is a big text box.",
modifier = Modifier
.size(200.dp)
.background(color = Color.Green)
.wrapContentSize(align = Alignment.Center),
textAlign = TextAlign.Center
)
}
}
Copy the code
In the code above, Text also has its own size, and Spacer is set to fill the size of the parent, but the parent has no specific size, so this property reversely scopes the parent so that the parent also fills the size of its parent. The end result is as follows: