第4章 Jetpack Compose提供了一系列的布局组件
Jetpack Compose是一种声明式UI框架,其中布局组件用于定义应用程序中各个UI元素的位置和大小。Jetpack Compose提供了一系列的布局组件,包括:
- Box: 用于定位单个子元素,并设置其大小、边距和填充等属性。
- Column: 垂直地排列多个子元素,并可以设置它们的对齐方式和间距。
- Row: 水平地排列多个子元素,并可以设置它们的对齐方式和间距。
- Grid: 用于在网格状布局中排列子元素,可以设置行数、列数和单元格之间的间距。
- ConstraintLayout: 使用约束来定义子元素的位置和大小,可以创建复杂的布局。
这些组件可以嵌套使用以创建复杂的布局,并支持响应式设计,使得UI可以根据屏幕尺寸和方向进行动态调整。与传统的XML布局方式不同,Jetpack Compose的布局代码使用Kotlin编写,更加简洁、易读和易维护。
4.1 Box布局
Box是Jetpack Compose中的一个布局组件,用于在屏幕上放置单个元素并设置其大小、边距和填充等属性。可以使用Box组件来创建简单的布局或覆盖其他组件。
提示: Box 布局和 Android 原生中的 FrameLayout 布局进行一定程度的类比。它们都是用于在屏幕上放置单个元素,并设置其大小、边距和填充等属性。但是,Jetpack Compose 的 Box 布局相比于 Android 原生的 FrameLayout 布局,提供了更多的灵活性,例如支持响应式设计以及更加直观的布局代码编写方式。此外,Jetpack Compose 还支持 ConstraintLayout 布局,它提供了更为强大的约束功能,可以创建更复杂的布局结构。
使用Box布局示例代码如下:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Box(
// Box是一个可组合函数,它是一个容器,用于包含其他可组合函数。
modifier = Modifier
.width(100.dp) // 设置Box的宽度为100dp
.height(100.dp) // 设置Box的高度为100dp
.background(Color.Red) // 设置Box的背景颜色为红色
)
}
}
}
在这个示例中,使用Modifier(修饰符)属性链来设置Box的宽度、高度和背景颜色。这些Modifier指定了Box应如何绘制和排列。 .width(100.dp)
将Box的宽度设置为100dp,.height(100.dp)
将其高度设置为100dp,.background(Color.Red)
将Box的背景颜色设置为红色。
示例运行结果如下图所示。
4.2 使用Compose 修饰符
在4.1节的示例中使用了Modifier(修饰符),它是Jetpack Compose中用于修改可组合函数的属性和行为的类。通过将修饰符链接在一起,可以构建复杂的UI元素并将它们组合在一起。例如,开发者可以使用Modifier设置组件的大小、位置、背景颜色、边框、填充等。
修饰符可以被视为对可组合函数的修饰符,就像属性修饰符一样。每个可组合函数都有一个modifier参数,它接受一个Modifier类型的值。可以将多个modifier链接在一起,从而具有序列化的效果。例如,.width(100.dp).height(100.dp)
为组件设置宽度和高度。
以下是一些常用的修饰符:
-
padding
:设置组件的内边距 -
size
:设置组件的尺寸 -
background
:设置组件的背景颜色或背景图像 -
clickable
:添加点击事件 -
fillMaxWidth
和fillMaxHeight
:将组件的宽度或高度设置为最大可用空间 -
align
:设置组件在水平或垂直方向上的位置
下面是一个简单的例子,演示了如何在 Jetpack Compose 中使用修饰符来创建一个具有红色背景和白色文本的 Text
组件:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyText()
}
}
/**
* Composable 函数,用于创建一个包含文本的组件
*/
@Composable
fun MyText() {
Text(
modifier = Modifier
.fillMaxWidth() // 设置宽度为最大可用宽度
.padding(26.dp) // 设置内边距为26dp
.height(90.dp) // 设置高度为100dp
.background(Color.Red), // 设置背景颜色为红色
color = Color.White, // 设置文本颜色为白色
textAlign = TextAlign.Center, // 设置文本居中对齐
text = "Hello, Jetpack Compose!", // 显示的文本内容
fontSize = 24.sp // 设置字体大小为24sp
)
}
}
在这个示例中,我们使用了 Modifier.fillMaxWidth
来将 Text
的宽度设置为最大可用宽度,并使用 Modifier.padding
和 Modifier.height
修饰符来调整其内边距和高度。同时,我们还使用了 Modifier.background
修饰符来设置其背景颜色为红色、TextAlign.Center
来居中对齐文本、color
属性来设置文本颜色、text
属性来设置显示的文本内容以及 fontSize
属性来设置字体大小。
示例运行结果如下图所示。
4. 3 Column布局
Column布局是一种垂直方向上的线性布局方式,它允许开发者将多个组件按照从上到下的顺序排列。
Compose中使用Column布局非常简单,开发者可以使用Column函数来创建一个Column布局,并在其中添加任意数量的子元素。这些子元素将自动垂直排列,并根据需要自动换行。
例如,以下代码将创建一个包含三个文本元素的Column布局:
package com.zhijieketang.helloproj
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Column(modifier = Modifier.background(Color.Gray)) {
Text("第一行", modifier = Modifier.padding(16.dp))
Text("第二行", modifier = Modifier.padding(16.dp))
Text("第三行", modifier = Modifier.padding(16.dp))
}
}
}
}
在这个示例中,我们将modifier
参数传递给Column函数,并使用Modifier.background()
函数设置了灰色背景色。我们还使用Modifier.padding()
函数将每个子元素的内边距设置为16dp,以使它们与背景之间留出间距。
示例运行结果如下图所示,可以屏幕上看到一个带有灰色背景和三个文本元素的垂直列表,这些文本元素都具有16dp的内边距。
4.4 Row布局
Row布局是一种水平方向上的线性布局方式,它允许您将多个组件按照从左到右的顺序排列。
在Jetpack Compose中使用Row布局非常简单,开发者可以使用Row函数来创建一个Row布局,并在其中添加任意数量的子元素。这些子元素将自动水平排列,并根据需要自动换行。
例如,以下代码将创建一个包含三个文本元素的Row布局:
package com.zhijieketang.helloproj
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Row (modifier = Modifier.background(Color.Gray)) {
Text("第一列", modifier = Modifier.padding(16.dp))
Text("第二列", modifier = Modifier.padding(16.dp))
Text("第三列", modifier = Modifier.padding(16.dp))
}
}
}
}
示例运行结果如下图所示,可以看到一个带有三个文本元素的水平列表,它们按照从左到右的顺序排列,并且每个文本元素具有16dp的内边距。此外,它们的背景颜色为灰色。
4.5 把Column和Row布局组合起来
在Jetpack Compose中,通常使用Column和Row布局组合起来创建用户界面。使用Column可以使元素垂直放置,而使用Row则可以使元素水平对齐。通过将多个Column和Row组合在一起,可以构建出各种不同的布局,例如网格、表格等。
中创建两个Column布局,可以将它们放在一个Row布局中。以下代码将创建一个包含两个垂直排列的Column布局的水平行:
package com.zhijieketang.helloproj
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Text
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Row { // 水平排列的容器
Column( // 第一列容器
modifier = Modifier
.weight(1f) // 分配相等的空间
.padding(16.dp), // 添加填充
horizontalAlignment = Alignment.CenterHorizontally, // 居中对齐
) {
Text("第一列 - 第一行") // 第一列第一行的文本
Text("第一列 - 第二行") // 第一列第二行的文本
Text("第一列 - 第三行") // 第一列第三行的文本
}
Column( // 第二列容器
modifier = Modifier
.weight(1f) // 分配相等的空间
.padding(16.dp), // 添加填充
horizontalAlignment = Alignment.CenterHorizontally, // 居中对齐
) {
Text("第二列 - 第一行") // 第二列第一行的文本
Text("第二列 - 第二行") // 第二列第二行的文本
Text("第二列 - 第三行") // 第二列第三行的文本
}
}
}
}
}
在这个示例中,我们使用Row
函数来创建一个水平排列的布局,并在其中包含了两个Column
函数。每个Column
函数都有一个weight
属性,该属性用于指定每个列所占比例,以便根据需要调整它们的大小。此外,我们还通过设置horizontalAlignment
属性使得每个Column
函数内的文本居中对齐。
上述示例运行结果如下图所示,读者看到两个带有三个文本元素的列,它们按照从上到下的顺序排列,并且每个列都具有相同的宽度并居中对齐。
4.6 使用ConstraintLayout构建复杂布局
在Jetpack Compose中使用ConstraintLayout。虽然它不是Android原生的ConstraintLayout,但Jetpack Compose提供了自己的版本,称为ConstraintLayout Compose。
ConstraintLayout Compose提供了一种声明性方式来定义视图之间的约束关系,类似于传统的XML布局文件中的ConstraintLayout。开发者可以使用ConstraintLayout Compose定位和控制组合UI元素的位置和大小,并定义它们之间的关系。
使用ConstraintLayout Compose,开发者可以通过以下方式来定义UI元素之间的关系:
- 定义对齐方式:使用
modifier.align()
方法来指定UI元素相对于其他元素或父容器的位置。 - 定义边缘关系:使用
modifier.constrainAs()
方法来将UI元素绑定到某些边缘或者其他UI元素。 - 定义宽度和高度:使用
modifier.width()
和modifier.height()
方法来指定UI元素的宽度和高度。 - 定义权重关系:使用
modifier.weight()
方法来指定UI元素在填充其容器时所占的空间比例。
下面的代码片段展示了如何使用ConstraintLayout Compose来创建两个文本视图(TextView),并将它们垂直居中对齐,并且左侧文本视图的右边缘与右侧文本视图的左边缘对齐:
package com.zhijieketang.helloproj
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ConstraintLayout
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 使用 setContent 函数设置 Activity 的内容视图为 LeftRightText
setContent {
LeftRightText()
}
}
}
// 定义一个 Composable 函数,用于显示两个 Text 组件,并使用 ConstraintLayout 进行布局
@Composable
fun LeftRightText() {
ConstraintLayout(
modifier = Modifier.fillMaxSize() // 通过 fillMaxSize() 属性使 ConstraintLayout 占据整个父容器
) {
// 创建两个 Ref,用于在组件之间建立关系
val (leftText, rightText) = createRefs()
// 左侧 Text 组件
Text(
text = "Left Text", // 文本内容
modifier = Modifier
.constrainAs(leftText) { // 约束该组件的位置
top.linkTo(parent.top) // 上边缘与父容器上边缘对齐
bottom.linkTo(parent.bottom) // 下边缘与父容器下边缘对齐
start.linkTo(parent.start) // 左边缘与父容器左边缘对齐
}
.padding(16.dp) // 设置组件的 padding
.layoutId("leftText") // 为该组件指定 layoutId,用于在 ConstraintLayout 中建立关系
)
// 右侧 Text 组件
Text(
text = "Right Text", // 文本内容
modifier = Modifier
.constrainAs(rightText) { // 约束该组件的位置
top.linkTo(parent.top) // 上边缘与父容器上边缘对齐
bottom.linkTo(parent.bottom) // 下边缘与父容器下边缘对齐
end.linkTo(parent.end) // 右边缘与父容器右边缘对齐
start.linkTo(leftText.end) // 左边缘与左侧 Text 组件的右边缘对齐
}
.padding(16.dp) // 设置组件的 padding
.layoutId("rightText") // 为该组件指定 layoutId,用于在 ConstraintLayout 中建立关系
)
}
}
**注意:**为了使用ConstraintLayout Compose,需要在应用程序的 build.gradle
文件中添加以下依赖:
// 使用 ConstraintLayout Compose
implementation 'androidx.constraintlayout:constraintlayout-compose:1.0.0-beta02'
示例运行结果如下图所示。
4.7 内容槽与布局
**内容槽(Content Slot)**是与布局相关的概念。在 Jetpack Compose 中,内容槽可以让你定义一个带有可变数量和类型的子组件的布局容器。这是通过将内容槽定义为一个函数参数实现的,这个函数可以接受一个或多个子组件,并使用这些子组件创建布局。
4.7.1 声明槽API
槽API是Jetpack Compose 中的一种功能,它允许开发人员在可组合函数中定义一个或多个槽(也称为占位符),以便在调用该函数时传递其他可组合函数或组件。这样,开发人员就可以更灵活地使用可组合函数,而不是仅限于其预定义的内容。例如,如果一个可组合函数显示一列文本,但需要在其中插入一个图像,那么可以使用槽API将一个可组合图像传递到该函数中,并在适当的位置显示。
声明槽API需要在可组合函数的签名中添加一个或多个参数,这些参数接受其他可组合函数或组件作为参数。例如,以下是一个带有一个槽的可组合函数的签名:
@Composable
fun MyComposable(
content: @Composable () -> Unit,
) {
// ...
}
假设我们需要在列中显示三个项目,但是在调用可组合项之前不知道哪个可组合项将占据中间位置。解决此问题的方法是将中间可组合项作为槽打开,任何其他可组合项都可以放置在调用函数时。这被称为为可组合项提供槽API。
@Composable
fun MyColumn() {
Column {
Text("Top Text")
Text("Middle Text")
Text("Bottom Text")
}
}
为了将槽添加到可组合项中我们需要是指定可组合函数接受槽作为参数。这本质上是声明可组合项接受其他可组合项作为参数的情况。修改MyColumn可以组合函数,代码如下。
@Composable
fun MyColumn(middleContent: @Composable () -> Unit) {
Column {
Text("Top Text")
middleContent()
Text("Bottom Text")
}
}
现在,我们已经成功地为MyColumn可组合项声明了一个槽API。
4.7.2 调用槽API
在4.7.1节中成功声明了槽API本机我们介绍如何调用槽API。
假设我们需要如下的按钮可组合项出现在 middleContent 插槽中:
@Composable
fun ButtonDemo() {
Button(onClick = { }) {
Text("Click Me")
}
}
那么调用代码如下:
@Composable
fun MyScreenContent() {
MyColumn(middleContent = { ButtonDemo() })
}
在调用该函数时,可以通过将可组合函数或组件传递给 content
参数来填充槽。例如:
MyComposable {
Text("Hello, world!")
}
完整的代码如下:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
// 在 setContent 中设置界面内容为 MyScreenContent 函数的返回值
MyScreenContent()
}
}
// 定义一个可组合函数 MyColumn,接受一个中间内容的可组合函数作为参数
@Composable
fun MyColumn(middleContent: @Composable () -> Unit) {
Column {
Text("Top Text")
// 调用中间内容的可组合函数
middleContent()
Text("Bottom Text")
}
}
// 定义一个 ButtonDemo 可组合函数,显示一个带有文本的按钮
@Composable
fun ButtonDemo() {
Button(onClick = { }) {
Text("Click Me")
}
}
// 定义 MyScreenContent 可组合函数,调用 MyColumn 函数并传递 ButtonDemo 函数作为中间内容
@Composable
fun MyScreenContent() {
MyColumn(middleContent = { ButtonDemo() })
}
}
上述代码运行结果如下图所示:
槽API的优点是代码复用性更高,因为可组合函数可以被设计为更灵活地适应不同的用例,而不是仅限于其预定义的内容。此外,槽API还可以提高代码的可读性和可维护性,因为它允许开发人员更清晰地表达他们的意图。
4.8 本章总结
本章介绍了Jetpack Compose中的内容槽和布局机制。首先,布局是将多个UI元素组合在一起的机制,例如Column、Row、Box、ConstraintLayout等。
这些布局定义了子元素的排列方式和位置,但子元素的数量和类型是固定的。其次,内容槽是实现布局子元素灵活定义的概念。内容槽是通过在可组合函数中定义占位符(即槽),并在调用时填充的机制实现的。声明槽API需要在函数签名中增加参数表示槽,调用时传递组件填充。内容槽与各种布局组合使用,可以创建动态内容和可复用性强的布局。例如,如果Column显示三个Text,但中间需要是一个Button,那么内容槽使Column可以显示三个可变类型的子元素,其中两个Text和一个Button。内容槽大大增强了布局的灵活性和重用性。它让开发者可以定义复杂的布局,而无需硬编码所有UI元素。这减少了重复代码,提高了UI元素的重用性,使得Jetpack Compose成为构建Android UI的优秀框架。
总之,内容槽是Jetpack Compose实现布局复杂和灵活定义的核心概念。它让开发者可以创建动态内容和可重用性强的布局,这也是Jetpack Compose易于构建优美UI的原因之一。内容槽机制是Jetpack Compose的精髓,它赋予布局以灵活性,使UI元素易于重用,这些都使开发者可以高效构建出色UI。