最近项目中使用到了Compose,为此重新温习一下Compose相关的知识。
学习后,长时间不使用忘记的很快,为此记录学习的Compose的入门各种问题。
一问一答的方式。
为什么要使用compose ?compose有哪些好处?
基于view的工具包已经存在了十多年,设备的功能更加强,用户对设备的期望越来越高。基于google的调研判断 大家需要一个现代框架的现代的工具包。而且能够充分使用到kotlin的强大功能
googel想更加快速提供更多的功能和改进。
Jetpack compose 是一个声明式的未捆绑工具包
compose 直观、易用、功能更强大。
compose 开发更快速,代码更少
compose 方法参数使用数据驱动UI,界面就会保持同步
怎么在项目中使用Compoes?
有两种方式,
- 一个是针对新建的项目。
- 一个是对旧的项目添加Compose支持。
方式一:创建支持 Compose 的新应用
使用Android Studio 提供新项目模板,
- 使用Android Studio ,请从菜单栏中依次选择 File > New > New Project。
- 在 Select a Project Template 窗口中,选择 Empty Activity,然后点击 Next。

方式二:现有应用设置 Compose
第一步:修改 build.gradle.kts (Module: app)
android {
// ... 其他配置保持不变 ...
buildFeatures {
// 1. 开启 Compose 功能
compose = true
}
composeOptions {
// 2. 指定 Compose 编译器版本 (需要与你的 Kotlin 版本匹配)
// 你当前使用 kotlin-stdlib:1.9.0,建议使用如下版本:
kotlinCompilerExtensionVersion = "1.5.1"
}
}
dependencies {
// ... 原有依赖保持不变 ...
// 3. 添加 Compose 核心依赖
val composeBom = platform("androidx.compose:compose-bom:2024.02.00") // 使用 BOM 管理版本
implementation(composeBom)
androidTestImplementation(composeBom)
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.compose.material3:material3") // 或者使用 material (Material 2)
// 这里的 activity-compose 版本需要 1.5.0+,你的 context 中 activity 是 1.5.0,最好升级一下
implementation("androidx.activity:activity-compose:1.8.2")
debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest")
}
修改后点击 "Sync Now" 同步项目。
第二步:修改 MainActivity.kt
将原来的 XML 布局加载方式 (setContentView(R.layout.activity_main)) 替换为 Compose 的 setContent。
// 1. 继承改为 ComponentActivity (AppCompatActivity 也可以,但 ComponentActivity 更轻量且原生支持 Compose)
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
// 2. 使用 setContent 替代 setContentView
setContent {
// 这里是 Compose 的入口
// 通常会包裹在一个 Theme 中,例如 MyApplicationTheme { ... }
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting(
name = "Android",
modifier = Modifier.padding(innerPadding)
)
}
}
}
}
// 3. 定义一个 Composable 函数
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Text(
text = "Hello $name!",
modifier = modifier
)
}
// 4. 添加预览
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
Greeting("Android")
}
模板代码setContent 是是怎么如何理解?如何使用?
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// enableEdgeToEdge()
// 注意这里使用的是 闭包,{} 而非 ()
setContent {
MyApplicationTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
MessageCard("tom", Modifier.padding(innerPadding))
}
}
}
}
}
@Composable
fun MessageCard(name: String, modifier: Modifier = Modifier) {
Text(
text = "Hello $name welcome compose world!",
modifier = modifier.width(200.dp).height(30.dp)
.clickable(onClick = {
Log.d("MainActivity", "MessageCard: $name")
}),
maxLines = 1,
fontSize = 20.sp,
color = Color.Blue,
)
}
@Preview()
@Composable
fun MessageCardPreview() {
Text(
text = "Hello manman welcome compose world!",
)
}
MyApplicationTheme是什么?
对应android view的主题样式

Scaffold是什么
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
// 直接使用 Compose组件
Greeting(
name = "Compose",
modifier = Modifier
.padding(innerPadding)
.background(color = Color.Yellow)
)
// 通过 Scaffold 的包装使用组件
// Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
// Greeting(
// name = "Compose",
// modifier = Modifier.padding(innerPadding)
// )
// }
}
}
先对比效果: 注意添加 enableEdgeToEdge() ,不添加两者没有区别。
enableEdgeToEdge() 是一个辅助函数,它能让您的应用内容显示在系统栏(即屏幕顶部的状态栏和底部的导航栏)后面,从而实现所谓的"边到边"效果。
这会给用户带来更加沉浸式的现代化界面体验。
调用这个函数后,它会自动将系统栏设置为半透明,并处理好窗口边衬区(Window Insets),确保您的布局能够正确响应系统栏的存在,避免重要的UI元素被遮挡。
不使用 Scaffold

使用Scaffold

对比效果看出,不使用显示的文字都覆盖到系统状态栏上面了
Scaffold (脚手架)组件,是 Compose Material 3 库提供的一个高级布局组件。提供了一个标准的屏幕结构,让你可以轻松地组合常见的 UI 元素,
如顶部栏 、底部栏 、浮动按钮等
可以把它想象成一个房子的框架,它已经为你预留好了放窗户(TopAppBar)、门(BottomAppBar)、阳台(FloatingActionButton)和主要居住空间(Content)的位置。
完整体的页面

代码
@OptIn(ExperimentalMaterial3Api::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
var presses by remember { mutableIntStateOf(0) }
// 通过 Scaffold 的包装使用组件
Scaffold(
// 1、放置顶部栏
topBar = {
TopAppBar(
title = {
Text("My app")
},
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.primaryContainer,
titleContentColor = MaterialTheme.colorScheme.primary,
)
)
},
// 2、放置底部栏
bottomBar = {
BottomAppBar(
containerColor = MaterialTheme.colorScheme.primaryContainer,
contentColor = MaterialTheme.colorScheme.primary
) {
Text(
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
text = "Bottom bar"
)
}
},
// 3、放置浮窗操作按钮
floatingActionButton = {
FloatingActionButton(onClick = { presses++ }) {
Icon(Icons.Default.Add, contentDescription = "Add")
}
},
modifier = Modifier.fillMaxSize()
) { innerPadding ->
Greeting(
name = "Compose $presses",
modifier = Modifier.padding(innerPadding)
)
}
}
}
@Preview() 怎么无法预览
@Preview()
@Composable
fun MessageCardPreview(name: String) {
Text(
text = "Hello $name welcome compose world!",
)
}
// 方式一:创建一个新的、专门用于预览的无参函数
// 方式二:创建有参的 带有默认值的 预览函数
@Preview(showBackground = true) // 推荐加上 showBackground = true,方便查看
@Composable
fun MessageCardPreview(name: String = "compose") {
Text(
text = "Hello $name welcome compose world!",
)
}
带有参数的 Composable 函数无法直接生成预览。
预览功能需要一个无参的 Composable 函数来调用,因为它不知道该为 name: String 这个参数传入什么值。
Compose方法中入参一定要带 Modifier吗?
@Composable
fun MessageCard(name: String, modifier: Modifier = Modifier) {
Text(
text = "Hello $name welcome compose world!",
maxLines = 1,
fontSize = 20.sp,
color = Color.Blue,
)
}
将 Modifier 作为参数,它允许调用者(使用你组件的地方)从外部自定义该组件的外观、布局和行为,而无需修改组件的内部代码。
- 有 Modifier:你的 Greeting 组件可以在任何地方使用。调用者可以决定它的大小、位置、背景、边距、点击事件等。
- 组件的内部逻辑(显示什么,比如 "Hello $name!")与其外部样式(它在布局中的位置、大小、装饰)是分开的。
- 几乎所有的官方 Material Design 组件(Text, Button, Column, Scaffold 等)都接受 Modifier 参数,保持一致性会让你的代码库更易于理解和维护。
Text怎么设置宽高?和view不太一样
@Composable
fun MessageCard(name: String, modifier: Modifier = Modifier) {
Text(
text = "Hello $name welcome compose world!",
modifier = modifier.width(200.dp).height(30.dp)
.clickable(onClick = {
Log.d("MainActivity", "MessageCard: $name")
}),
maxLines = 1,
fontSize = 20.sp,
color = Color.Blue,
)
}
理解使用 Modifier
Modifier 是 Jetpack Compose 中用于装饰和改变可组合项的构建块,它是一个不可变的有序元素集合,允许你控制组件的布局属性、行为和外观。
主要用途
- 布局控制
- 设置尺寸:fillMaxSize()、fillMaxWidth()、width()、height()
- 设置间距:padding()
- 设置权重:weight()
- 视觉效果
- 背景颜色:background(Color.Red)
- 边框:border()
- 圆角:clip(RoundedCornerShape(8.dp))
- 交互处理
- 点击事件:clickable {}
- 触摸反馈:combinedClickable()
Compose 中的三个基本标准布局元素是 Column、Row 和 Box 可组合项。
Column、Row 和 Box 类似于传统 Android 开发中的 垂直LinearLayout 、水平LinearLayout 和 FrameLayout

为什么Compose 中的状态保存一定要用 remember?
以官方Demo 为例:
1、直接使用 var expanded = false 变量设置不同的值不会使 Compose 将其检测为状态更改,
因此不会产生任何效果。
原因:
更改此变量不会触发重组的原因是Compose 并未跟踪此更改。此外,每次调用 Greeting 时,都会将该变量重置为 false。
Compose 应用通过调用可组合函数将数据转换为界面。如果您的数据发生变化,Compose 会使用新数据重新执行这些函数, 从而创建更新后的界面,此过程称为重组。Compose 还会查看各个可组合项需要哪些数据, 以便只需重组数据发生了变化的组件,而避免重组未受影响的组件。
2、如需向可组合项添加内部状态,可以使用 mutableStateOf 函数,该函数可让 Compose 重组读取该 State 的函数。
不能只是将 mutableStateOf 分配给可组合项中的某个变量。
val expanded = mutableStateOf(false) ,同样 不会产生任何效果。
3、重组后保留状态,请使用 remember 记住可变状态。
remember 可以起到保护作用,防止状态在重组时被重置。
如果从屏幕的不同部分调用同一可组合项,则会创建不同的界面元素,且每个元素都会拥有自己的状态版本。
可以将内部状态视为类中的私有变量。
@Composable
fun Greeting() {
// 1、
// var expanded = false
// 2、
// val expanded = mutableStateOf(false)
// 3、
val expanded = remember { mutableStateOf(false) }
val extraPadding = if (expanded.value) 48.dp else 0.dp
Surface(color = Color.Yellow, modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp)) {
Row(modifier = Modifier.padding(24.dp)) {
Column(modifier = Modifier
.weight(1f)
.padding(bottom = extraPadding)) {
Text(text = "Hello")
Text(text = "World")
}
ElevatedButton(
onClick = { expanded.value = !expanded.value }
) {
Text(if (expanded.value) "Show less" else "Show more")
}
}
}
}