一、前言
这是 Jetpack Compose 系列的第五篇。前面四篇我们介绍了Compose的背景、完成了第一个Compose程序、理解了Composable与Modifier的核心机制,还深入学习了Modifier的各种修饰符。今天,让我们用实战的方式,系统学习Compose中最常用的UI组件。
Compose的组件体系和传统的 Android View 一样:布局组件决定UI怎么排列,基础组件决定UI长什么样,列表组件决定大量数据怎么展示。下面逐一展示给大家。
二、布局容器
传统 Android UI开发用 XML 写布局(LinearLayout、RelativeLayout和 FrameLayout),Compose 则使用Kotlin函数描述布局。最常用的三个容器是 Column、Row 和 Box。
Column——纵向排列,子组件从上到下依次摆放。
@Composable
fun ColumnExample() {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Text("北京")
Text("上海")
Text("广州")
}
}
Row——横向排列,子组件从左到右依次摆放。
@Composable
fun RowExample() {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceEvenly
) {
Text("左")
Text("中")
Text("右")
}
}
Box——层叠布局,子组件堆叠在一起,类似传统View 体系的FrameLayout。
@Composable
fun BoxExample() {
Box(
modifier = Modifier.size(150.dp),
contentAlignment = Alignment.Center
) {
Box(modifier = Modifier
.fillMaxSize()
.background(Color.Yellow))
Text("文字居中", color = Color.White)
}
}
💡Column 和 Row负责”排列“,Box 负责”叠加“。三者配合可以构建绝大多数界面布局。
布局效果如下:

三、基础组件
3.1 Text
Text 文本组件,用来显示文本,替代传统的TextView,支持丰富的样式配置。如:
- fontWeight:文本的粗细
- fontFamily:文本的字体
- fontStyle:文本的变体(Italic/Normal)
- lineHeight:行高
- …
除了直接给 Text 传入要显示的文字内容外,Compose 同样支持 R资源 的方式获取字符串——stringResource() 。
@Composable
fun TextExample() {
// 指定字符串
Text(
text = "Hello Compose!",
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
color = Color.Blue,
text = TextAlign.Center,
maxLines = 1,
modifier = Modifier.padding(16.dp)
)
// 指定文字资源
Text(
text = stringResource(R.string.hello_world)
)
}
3.2 Image
Image 图片组件,显示图片,替代传统的 ImageView。支持3种类型的图片设置:
- ImageVector:矢量图对象,可以显示SVG格式的图
- ImageBitmap:位图对象,可以显示JPG、PNG等格式图
- Painter:自定义画笔,可以在Canvas上直接绘制图
Image 组件支持直接传递实例参数,但一般开发中我们优先使用 res/drawable 下的资源图。
@Composable
fun ImageSample() {
Column() {
Image(
painter = painterResource(id = R.drawable.ic_weather_sun),
contentDescription = "晴",
)
Image(
bitmap = ImageBitmap.imageResource(id = R.drawable.pic_2),
contentDescription = null
)
// 本地图片放在 res/drawable 目录下,通过 painterResource 加载的示例。
Image(
imageVector = ImageVector.vectorResource(id = R.drawable.ic_search),
contentDescription = "矢量图资源"
)
}
}
图片显示效果如下:

熟悉传统 Android View的小伙伴肯定熟悉 scaleType,它用来指定图片的伸缩样式(拉伸、裁剪、留白还是保持原样),Compose的 Image 同样有提供这样的功能——contentScale参数。
| ContentScale类型 | 说明 |
|---|---|
| ContentScale.Crop | 按比例缩放使图片完全覆盖容器,超出部分裁剪,类似ScaleType.centerCrop |
| ContentScale.Fit | 按比例缩放使图片完整显示在容器内,居中,类似ScaleType.fitCenter |
| ContentScale.Inside | 完整显示且不放大,仅当图片大于容器时缩小,类似ScaleType.centerInside |
| ContentScale.FillBounds | 不保持比例拉伸填满容器,类似ScaleType.fitXY |
| ContentScale.FillWidth | Compose独有,按比例缩放使图片宽度填满容器,高度自适应 |
| ContentScale.FillHeight | Compose独有,按比例缩放使图片高度填满容器,宽度自适应 |
| ContentScale.None | 不缩放,原尺寸居中绘制,过大裁剪/过小留白,类似ScaleType.center |

3.3 Button
Button 按钮组件,处理点击交互用,Material 3 提供了多种的按钮样式。
@Composable
fun ButtonExample() {
var clickCount by remember { mutableStateOf(0) }
Button(
onClick = { clickCount++ },
modifier = Modifier.padding(16.dp)
) {
Text(
text = "点击次数: $clickCount",
fontSize = 18.sp,
modifier = Modifier.padding(2.dp)
)
}
}
Button 的点击事件通过 onClick 参数传入,组件内部状态用remember { mutableStateOf()} 管理。
四、列表
LazyColumn ——高性能列表,类似传统Android View中的 RecyclerView。
@Composable
fun LazyColumnExample() {
val items = List(50) { "条目 #$it" }
LazyColumn(modifier = Modifier.fillMaxSize()) {
items(items) { item ->
Text(
text = item,
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
)
}
}
}
运行代码效果如下:

五、页面骨架
Scaffold —— Material Design 页面骨架,内置了TopAppBar、FloatingActionButton 、NavigationBar等。
@Composable
fun ScaffoldExample() {
var fabClickCount by remember { mutableStateOf(0) }
Scaffold(
topBar = {
TopAppBar(title = { Text("我的应用") })
},
floatingActionButton = {
FloatingActionButton(onClick = { fabClickCount++ }) {
Icon(Icons.Default.Add, contentDescription = "添加")
}
}
) { innerPadding ->
Box(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding),
contentAlignment = Alignment.Center
) {
Text("FAB点击: $fabClickCount 次")
}
}
}
效果示意图如下:

六、综合实践
看了这么多单个组件用法,下面让我们来完成综合学习——使用上面提到的组件实现一个天气主界面:
private val city = "北京"
private val currentTemp = "28°C"
private val weatherCondition = "晴"
private val feelsLike = "26°C"
private val humidity = "65%"
private val wind = "12 km/h"
private val hourlyList = listOf(
"现在 28°", "13:00 29°", "14:00 30°", "15:00 29°", "16:00 28°"
)
private val dailyList = listOf(
"今天 30°/22° 晴",
"周一 28°/20° 多云",
"周二 26°/19° 小雨",
"周三 27°/21° 晴",
"周四 29°/23° 多云",
"周五 31°/24° 晴",
"周六 30°/22° 阴"
)
@Composable
fun SimpleWeatherScreen() {
Scaffold { innerPadding ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = city,
fontSize = 24.sp,
fontWeight = FontWeight.Bold
)
}
Spacer(modifier = Modifier.height(20.dp))
Box(
modifier = Modifier
.fillMaxWidth()
.height(150.dp),
contentAlignment = Alignment.Center
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Image(
painter = painterResource(id = android.R.drawable.ic_menu_gallery),
contentDescription = "天气图标",
modifier = Modifier.size(60.dp)
)
Text(
text = currentTemp,
fontSize = 40.sp,
fontWeight = FontWeight.Bold
)
Text(text = weatherCondition, fontSize = 18.sp)
}
}
Spacer(modifier = Modifier.height(16.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = feelsLike, fontWeight = FontWeight.Bold)
Text(text = "体感", fontSize = 12.sp, color = Color.Gray)
}
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = humidity, fontWeight = FontWeight.Bold)
Text(text = "湿度", fontSize = 12.sp, color = Color.Gray)
}
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = wind, fontWeight = FontWeight.Bold)
Text(text = "风速", fontSize = 12.sp, color = Color.Gray)
}
}
Spacer(modifier = Modifier.height(20.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
hourlyList.take(5).forEach { item ->
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = item.split(" ")[0], fontSize = 12.sp)
Image(
painter = painterResource(id = android.R.drawable.ic_menu_gallery),
contentDescription = null,
modifier = Modifier.size(24.dp)
)
Text(text = item.split(" ")[1], fontSize = 12.sp)
}
}
}
Spacer(modifier = Modifier.height(20.dp))
LazyColumn(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
items(dailyList) { dayInfo ->
val parts = dayInfo.split(" ")
val day = parts[0]
val temp = parts[1]
val condition = parts[2]
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(text = day, fontWeight = FontWeight.Medium)
Row {
Text(text = condition)
Spacer(modifier = Modifier.width(8.dp))
Text(text = temp)
}
}
}
}
}
}
}
感兴趣的读者可以直接Copy上面的代码在 AndroidStudio 里运行,最后的效果类似下面:

七、 总结
本篇内容覆盖了 Compose 日常开发中最核心的几类组件:布局容器(Column/Row/Box)、基础组件(Text/Image/Button)、列表(LazyColumn)和页面骨架(Scaffold)。
掌握了这些组件后足以支撑起一个简单但完整APP的UI开发,如待办事项APP、天气APP、新闻APP等。
🚨 最后提醒,本文示例使用的为 Material Design 3。
2015

被折叠的 条评论
为什么被折叠?



