
Jetpack Compose 是 Google 推出的现代化 Android 声明式 UI 工具包,使用 Kotlin 语言构建,彻底改变了传统基于 XML 的 UI 开发方式。它以 声明式、函数式 和 响应式 的方式编写界面,让 UI 开发更简洁、高效、可组合。
本教程带你从零开始,系统掌握 Compose 的核心概念与高级技巧。
声明式 UI:描述“UI 应该是什么样”,而非“如何构建 UI”
Kotlin First:纯 Kotlin 编写,充分利用语言特性
可组合函数:小部件自由组合,高度可复用
实时预览:Android Studio 支持 @Preview 注解,无需运行 App
响应式:状态变化自动更新 UI
性能优秀:智能重组(Recomposition),减少不必要的绘制
所有 UI 组件都是用 @Composable 注解的函数。
@Composable
fun Greeting(name: String) {
Text(text = "Hello, $name!")
}传统方式(命令式):
val textView = findViewById<TextView>(R.id.text)
textView.text = "Hello"Compose 方式(声明式):
Text(text = "Hello")你声明 UI 的状态,Compose 负责更新。
当状态变化时,Compose 会自动重新调用可组合函数(重组)。
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Button(onClick = { count++ }) {
Text("Clicked $count times")
}
}remember:在重组期间保留状态 mutableStateOf:创建可观察状态,变化时触发重组 推荐使用 ViewModel 管理 UI 状态:
@Composable
fun Counter(viewModel: CounterViewModel) {
val count by viewModel.count.collectAsState()
Button(onClick = { viewModel.increment() }) {
Text("Clicked $count times")
}
}Text(
text = "Hello Compose",
fontSize = 24.sp,
fontWeight = FontWeight.Bold,
color = Color.Blue
)Image(
painter = painterResource(R.drawable.ic_logo),
contentDescription = "Logo",
contentScale = ContentScale.Fit
)Button(onClick = { /* handle click */ }) {
Text("Click Me")
}var text by remember { mutableStateOf("") }
TextField(
value = text,
onValueChange = { text = it },
label = { Text("Enter text") }
)Compose 提供了强大的布局容器。
Column(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Item 1")
Text("Item 2")
Button(onClick = {}) { Text("OK") }
}Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text("Left")
Text("Right")
}Box {
Image(painter = ..., contentDescription = null)
Text("Overlay Text", modifier = Modifier.align(Alignment.Center))
}ConstraintLayout {
val (text, button) = createRefs()
Text(
"Hello",
modifier = Modifier.constrainAs(text) {
top.linkTo(parent.top)
start.linkTo(parent.start)
}
)
Button(
onClick = { },
modifier = Modifier.constrainAs(button) {
bottom.linkTo(parent.bottom)
end.linkTo(parent.end)
}
) {
Text("Click")
}
}需添加依赖:implementation “androidx.constraintlayout:constraintlayout-compose:1.0.1”
Modifier 是 Compose 的核心设计模式,用于修饰组件的外观和行为。
Text(
"Hello",
modifier = Modifier
.padding(16.dp)
.fillMaxWidth()
.background(Color.Gray)
.clickable { /* handle click */ }
.border(2.dp, Color.Black)
)Modifier | 作用 |
|---|---|
padding() | 内边距 |
fillMaxWidth() / fillMaxHeight() | 填充父容器 |
size(width, height) | 设置大小 |
background() | 背景颜色或形状 |
clickable { } | 点击事件 |
border() | 边框 |
clip() | 裁剪形状(如 RoundedCornerShape(8.dp)) |
顺序很重要!padding().background() 与 background().padding() 效果不同。
@Composable
fun MessageList(messages: List<String>) {
LazyColumn {
items(messages) { message ->
MessageItem(message)
}
}
}LazyRow {
items(10) { index ->
Chip(text = "Item $index")
}
}Lazy 前缀表示“懒加载”,只渲染可见项,性能优秀。
MaterialTheme(
colorScheme = ColorScheme.Light(
primary = Color.Blue,
secondary = Color.Green
),
typography = Typography(
bodyLarge = TextStyle(fontSize = 18.sp)
),
shapes = Shapes(
medium = RoundedCornerShape(8.dp)
)
) {
// Your UI here
Greeting("Android")
}Text(
"Themed Text",
style = MaterialTheme.typography.headlineMedium,
color = MaterialTheme.colorScheme.primary
)使用 Navigation Component + Compose。
implementation "androidx.navigation:navigation-compose:2.7.6"@Composable
fun NavGraph() {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "home") {
composable("home") { HomeScreen() }
composable("profile") { ProfileScreen() }
composable("detail/{id}") { backStackEntry ->
val id = backStackEntry.arguments?.getString("id")
DetailScreen(id)
}
}
}// 前进
navController.navigate("profile")
// 带参数
navController.navigate("detail/123")
// 返回
navController.popBackStack()处理生命周期、数据加载等副作用。
Effect | 用途 |
|---|---|
LaunchedEffect | 在作用域内启动协程 |
DisposableEffect | 资源释放(如订阅) |
rememberCoroutineScope | 获取协程作用域 |
SideEffect | 将状态同步到非 Compose 代码 |
@Composable
fun MyScreen(viewModel: MyViewModel) {
val coroutineScope = rememberCoroutineScope()
LaunchedEffect(Unit) {
viewModel.loadData()
}
DisposableEffect(key1 = "connection") {
val connection = connect()
onDispose { connection.disconnect() }
}
}@Composable
fun MyCustomLayout(
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
Layout(
content = content,
modifier = modifier
) { measurables, constraints ->
// 手动测量和布局子项
layout(width, height) {
// 放置子项
}
}
}var enabled by remember { mutableStateOf(true) }
val backgroundColor by animateColorAsState(
targetValue = if (enabled) Color.Green else Color.Red,
tween(durationMillis = 300)
)
Box(
modifier = Modifier
.size(100.dp)
.background(backgroundColor)
.clickable { enabled = !enabled }
)在 Compose 中使用 View:
AndroidView(
factory = { context ->
WebView(context).apply {
loadUrl("https://example.com")
}
}
)在 Activity 中使用 Compose:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyAppTheme {
Greeting("Android")
}
}
}
}必须做:
避免: