从 0 到 1:使用 Jetpack Compose 和智能自动化实现高效 Android UI 开发

发布于:2025-07-20 ⋅ 阅读:(12) ⋅ 点赞:(0)

现代 Android UI 开发正逐步从命令式 XML 向声明式 Compose 转变。Compose 凭借其简洁、高效、易测试的特点,能够让开发者更专注于界面和业务逻辑,而不必陷入大量模板化的代码。手把手带你构建一个完整的 Todo List 应用,并演示如何借助自动化工具大幅提升开发效率。


目录

  1. 为什么选用 Jetpack Compose?
  2. 环境与依赖准备
  3. Compose 基础入门
  4. 状态管理:驱动 UI 的心脏
  5. 布局与主题:打造统一的视觉风格
  6. 进阶:副作用、性能与动画
  7. 智能自动化:让重复工作“自动跑起来”
  8. 实战:构建 Todo List 应用
  9. 测试与 CI:质量保障
  10. 性能优化与最佳实践
  11. 总结与展望
  12. 参考文献

为什么选用 Jetpack Compose?

  • 声明式:写 UI 就像写函数,传入数据,输出界面,避免了 XML + findViewById 的繁琐。
  • 可组合:所有 UI 元素都是 @Composable 函数,易于拆分、复用和测试。
  • 内建状态管理:结合 Kotlin 特性,remembermutableStateOf 等 API,让状态驱动界面更新,重组(recomposition)高效且可预测。
  • 生态完善:官方提供 Material3 组件、Accompanist 辅助库、与导航、生命周期等 Jetpack 库深度集成。

环境与依赖准备

  1. Android Studio:推荐使用 Arctic Fox (2020.3.1) 或更高版本。

  2. Kotlin:版本 1.8.0+。

  3. Gradle 配置

    // 项目级 build.gradle
    buildscript {
        ext {
            compose_version   = '1.5.0'
            kotlin_version    = '1.8.0'
        }
        dependencies {
            classpath "com.android.tools.build:gradle:8.0.0"
            classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        }
    }
    
    // 模块级 build.gradle
    plugins {
        id 'com.android.application'
        id 'org.jetbrains.kotlin.android'
        id "kotlin-kapt"
        id "io.github.raamcosta.compose-destinations" version "1.8.5"
    }
    
    android {
        compileSdk 34
        defaultConfig {
            applicationId "com.example.todo"
            minSdk 21
            targetSdk 34
        }
        buildFeatures {
            compose true
        }
        composeOptions {
            kotlinCompilerExtensionVersion compose_version
        }
        kotlinOptions {
            jvmTarget = "1.8"
        }
    }
    
    dependencies {
        implementation "androidx.core:core-ktx:1.10.1"
        implementation "androidx.compose.ui:ui:$compose_version"
        implementation "androidx.compose.material3:material3:1.1.0"
        implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.0"
        implementation "androidx.activity:activity-compose:1.7.0"
        implementation "io.github.raamcosta.compose-destinations:animations-core:1.8.5"
        kapt "io.github.raamcosta.compose-destinations:ksp:1.8.5"
    }
    

    Tips: 在 Android Studio 设置里(Preferences → Experimental)确保已开启 Compose 支持。


Compose 基础入门

认识 @Composable

@Composable
fun Greeting(name: String) {
    Text(text = "Hello, $name!")
}
  • 声明式:直接描述“界面应该是什么样子”,数据变化时 Compose 自动重组。
  • 无 UI 对象:没有 ViewActivity 等概念,只有函数调用嵌套。

布局容器

  • Column:垂直排列
  • Row:水平排列
  • Box:堆叠布局
@Composable
fun ProfileCard() {
    Row(modifier = Modifier.padding(16.dp)) {
        Image(/*…*/)
        Column(modifier = Modifier.padding(start = 8.dp)) {
            Text("Alice")
            Text("Android Developer")
        }
    }
}

修饰符(Modifier

  • 链式调用.fillMaxWidth().padding(8.dp).background(Color.Gray)
  • 常用APIsize(), padding(), background(), clickable()……

状态管理:驱动 UI 的心脏

mutableStateOfremember

@Composable
fun Counter() {
    var count by remember { mutableStateOf(0) }
    Button(onClick = { count++ }) {
        Text("Clicked $count times")
    }
}
  • remember:组件重组时保留状态
  • mutableStateOf:包装可观察状态,值变时自动触发重组

StateFlow 结合 Compose

在 ViewModel 中:

class TodoViewModel : ViewModel() {
    private val _todos = MutableStateFlow<List<Todo>>(emptyList())
    val todos: StateFlow<List<Todo>> = _todos
    fun load() { /*…*/ }
}

在 Composable:

@Composable
fun TodoList(viewModel: TodoViewModel = hiltViewModel()) {
    val list by viewModel.todos.collectAsState()
    LazyColumn { items(list) { TodoItem(it) } }
}

布局与主题:打造统一的视觉风格

Material3 主题

@Composable
fun MyTheme(content: @Composable ()->Unit) {
    MaterialTheme(
        colorScheme = lightColorScheme(
            primary = Color(0xFF6200EE),
            secondary = Color(0xFF03DAC6)
        ),
        typography = Typography(/*…*/),
        content = content
    )
}

自定义组件

@Composable
fun MyButton(text: String, onClick: ()->Unit) {
    Button(
        onClick = onClick,
        shape = RoundedCornerShape(8.dp),
        modifier = Modifier.fillMaxWidth()
    ) {
        Text(text.uppercase(), fontWeight = FontWeight.Bold)
    }
}

进阶:副作用、性能与动画

副作用 API

  • LaunchedEffect(key):基于键启动协程
  • SideEffect:在每次成功重组后执行
  • DisposableEffect:绑定 / 解绑资源

性能优化

  • remember 缓存计算结果
  • derivedStateOf:避免不必要重组
  • 拆分小组件:让重组局部化

简单动画

@Composable
fun PulsingDot() {
    val scale by animateFloatAsState(
        targetValue = if (expanded) 1.5f else 1f,
        animationSpec = tween(1000, easing = LinearEasing)
    )
    Box(modifier = Modifier.size(50.dp).graphicsLayer { scaleX = scale; scaleY = scale }
        .background(Color.Red, CircleShape)
        .clickable { expanded = !expanded })
}

智能自动化:让重复工作“自动跑起来”

  1. Live Templates

    • 在 IDE 中预设 Compose 代码片段,如 @cmp:

      @Composable
      fun $NAME$() {
          $END$
      }
      
    • 极大减少样板代码输入。

  2. Compose Destinations

    • 注解一行:@Destination
    • 编译时自动生成导航:无须手写 NavHost、NavController。
  3. GitHub Copilot / AI 助手

    • 在代码中写注释 // TODO: fetch from repo
    • Copilot 自动补全网络请求、JSON 解析等模板。
  4. Screenshot Testing(Paparazzi)

    • 快速对比 UI 回归,自动化生成、校对截图。

实战构建 Todo List 应用

项目结构

com.example.todo
├── MainActivity.kt
├── navigation/           // Destinations 生成
├── ui/
│   ├── TodoListScreen.kt
│   └── EditTodoScreen.kt
└── viewmodel/
    └── TodoViewModel.kt

关键代码

TodoListScreen.kt
@Destination(start = true)
@Composable
fun TodoListScreen(
    navigator: DestinationsNavigator,
    viewModel: TodoViewModel = hiltViewModel()
) {
    val todos by viewModel.todos.collectAsState()
    Scaffold(
        topBar = { TopAppBar(title = { Text("我的待办") }) },
        floatingActionButton = {
            FloatingActionButton(onClick = { navigator.navigate(EditTodoScreenDestination(null)) }) {
                Icon(Icons.Default.Add, contentDescription = "添加")
            }
        }
    ) { padding ->
        LazyColumn(modifier = Modifier.padding(padding)) {
            items(todos) { todo ->
                Card(modifier = Modifier
                    .fillMaxWidth()
                    .padding(8.dp)
                    .clickable { navigator.navigate(EditTodoScreenDestination(todo.id)) }
                ) {
                    Text(todo.title, modifier = Modifier.padding(16.dp))
                }
            }
        }
    }
}
TodoViewModel.kt
@HiltViewModel
class TodoViewModel @Inject constructor(
    private val repo: TodoRepository
): ViewModel() {
    private val _todos = MutableStateFlow<List<Todo>>(emptyList())
    val todos: StateFlow<List<Todo>> = _todos

    init { loadTodos() }

    fun loadTodos() = viewModelScope.launch {
        _todos.value = repo.getAllTodos()
    }

    fun save(todo: Todo) = viewModelScope.launch {
        repo.save(todo)
        loadTodos()
    }
}

测试与 CI:质量保障

  1. Compose UI Test

    @get:Rule val rule = createComposeRule()
    @Test fun addButtonNavigates() {
        rule.setContent { TodoListScreen(/*…*/) }
        rule.onNodeWithContentDescription("添加").performClick()
        rule.onNodeWithText("编辑待办").assertExists()
    }
    
  2. Paparazzi 截图测试

    @Test fun todoListSnapshot() = paparazzi.snapshot {
        TodoListScreen(/*…*/)
    }
    
  3. GitHub Actions

    • 运行 ./gradlew testDebugUnitTest./gradlew testDebugAndroidTest
    • 对比 Paparazzi 输出,回归提醒。

性能优化与最佳实践

  • 局部重组:把可变状态限制在最小组件里。
  • 使用 derivedStateOf:复杂计算结果缓存。
  • 避免在组合函数中做 I/O:利用 LaunchedEffect
  • 自定义 Layout:对于特殊需求可编写自己的布局算法。


网站公告

今日签到

点亮在社区的每一天
去签到