Android Compose快速入门手册(真的只是入门)

Compose出来有一段时间,很多人一直不愿意使用,一部分是之前的性能问题,还有一部分是很多项目其实是祖传的,谁也不敢冒然引入新技术,毕竟锅背起来很累。

写在文章开头:

目前这方面的文章很多,很多大佬写的很详细,分了很多章,确实非常精彩。但,很多时候我只是想先把技术用起来,并没有那么多时间让我慢慢品味,在使用中学习才是王道。同时也记录下这几天学习的成果,好记性不如烂笔头,那这篇文章就诞生。

如果你已经有了一定的Compose的基础,那这篇文章不看也罢。本文更像是知识点的流水账式的堆砌,主要让大家快速掌握一些基础概念,以防不时之需。

一、控件篇

  • Text
kotlin 复制代码
@Composable
fun SimpleText() {
    //蓝色、24sp、粗体、居中的文字
    Text(
        text = "Hello, Compose!",
        color = Color.Blue,
        fontSize = 24.sp,
        fontWeight = FontWeight.Bold,
        textAlign = TextAlign.Center
    )
}
  • Button
kotlin 复制代码
@Composable
fun SimpleButton() {
    Button(onClick = { /* 这里执行点击逻辑,比如 Toast */ }) {
        Text("点击我")
    }
}

按钮里面可以放任意 Composable,比如 Text 或 Icon。需要注意的是,Button本身不提供默认内容,需要手动填充 Text 或 Icon"

  • Image
    Image 用于加载图片,支持本地资源、网络 URL 或 Bitmap。Image不内置网络加载,但支持 painter。可以使用Coil和Glide实现网络加载,Compose里面我更支持Coil。
kotlin 复制代码
import androidx.compose.foundation.Image
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.painterResource
import coil.compose.rememberAsyncImagePainter // 需要添加 Coil 依赖

@Composable
fun SimpleImage() {
    // 本地图片
    Image(
        painter = painterResource(id = R.drawable.ic_launcher),
        contentDescription = "App Icon"
    )
    
    // 网络图片(用 Coil 库)
    Image(
        painter = rememberAsyncImagePainter("https://example.com/image.jpg"),
        contentDescription = "Network Image"
    )
}

Coil的更多的用法不再阐述了,感兴趣的小伙伴自己去问AI。

  • TextField / OutlinedTextField 描述:TextField是一个基本的文本输入框,允许用户输入文本,支持自定义标签、占位符等等。
    代码示例:
kotlin 复制代码
@Composable
fun TextFieldExample() {
    var text by remember { mutableStateOf("") }
    TextField(
        value = text,
        onValueChange = { text = it },
        label = { Text("请输入用户名") }, // 标签
        placeholder = { Text("例如:张三") } // 占位符
    )
}

@Preview
@Composable
fun TextFieldPreview() {
    TextFieldExample()
}

OutlinedTextField用法是与TextField大致相同,它们主要的区别就是OutlinedTextField 是 TextField的带边框变体,样式更现代.

  • Icon
    描述:Icon 用于显示矢量图标,通常与 Icons 库(如 Icons.Filled)一起使用,适合在按钮或输入框中添加视觉元素。 代码示例:
kotlin 复制代码
@Composable
fun IconExample() {
    Icon(
        imageVector = Icons.Filled.Favorite, // 使用内置的爱心图标
        contentDescription = "收藏图标" // 无障碍描述
    )
}
  • Switch
    描述:Switch 是一个开关控件,允许用户在两种状态(开/关)之间切换,适合设置选项
kotlin 复制代码
@Composable
fun SwitchExample() {
    var checked by remember { mutableStateOf(false) }
    Switch(
        checked = checked,
        onCheckedChange = { checked = it } // 切换状态时更新
    )
}
  • Card
    描述:Card 是一个带有阴影和圆角的容器,用于分组内容,适合展示卡片式布局。 代码示例:
kotlin 复制代码
@Composable
fun CardExample() {
    Card(
        modifier = Modifier.padding(16.dp) // 外边距
    ) {
        Text(
            text = "这是一个卡片示例",
            modifier = Modifier.padding(16.dp) // 内容内边距
        )
    }
}
  • Divider
    描述:Divider 是一个水平或垂直的分隔线,用于分隔内容区域。 代码示例:
kotlin 复制代码
@Composable
fun DividerExample() {
    Divider(
        thickness = 2.dp, // 分隔线厚度
        modifier = Modifier.padding(horizontal = 16.dp) // 水平边距
    )
}
  • Spacer
    描述:Spacer 是一个不可见的占位控件,用于在布局中添加间距,控制组件之间的距离,可以在效果上代替xml的margin效果 代码示例:
kotlin 复制代码
@Composable
fun SpacerExample() {
    Column {
        Text("第一行文本")
        Spacer(modifier = Modifier.height(16.dp)) // 添加16dp的垂直间距
        Text("第二行文本")
    }
}

更多:

Image和Icon的区别:

特性 Icon Image
用途 显示矢量图标或简单图像 显示位图、照片或复杂图像
颜色控制 支持tint改色 显示原始图像颜色,无tint属性
缩放裁剪 简单缩放,适合小图标 支持复杂缩放(如 ContentScale)
性能 轻量,适合频繁使用的小图标 更适合高质量、大型图像,稍重
使用场景 按钮、导航、装饰性图标 头像、背景、产品图片等

二、布局篇

  • Box
  • Column
  • Row
  • LazyColumn
  • LazyRow
  • LazyVerticalGrid
  • LazyHorizontalGrid
  • ConstraintLayout

布局就不详细介绍了,太占篇幅,我学习了下,发现也比较好上手,就不浪费时间了。

三、Modifier究竟是个什么东西

Modifier 是 Compose 的一个核心概念,是 Compose 的灵魂,掌握它就能灵活布局。 Modifier 像链条一样链接:.padding().background().clickable() 等。 简单说,Modifier 是个接口,提供了尺寸、间距、点击、拖拽等功能,可以无限制串联API。

主要负责4个方面的功能:

  • 修改Compose控件的外观(尺寸、布局、样式、行为),而不改变组件本身。
  • 为Compose控件添加额外的信息,如适配盲人或弱视的无障碍标签。
  • 处理用户的事件输入(非TextField文本输入),如点击、滑动、拖动等等,
  • 让不可交互控件变得可交互(可点击、可滚动、可拖拽等),如Box控件变得可点击。

这正是不可变对象的设计:每次操作返回新实例,原对象不变,那举个例子,大家就明白了。

kotlin 复制代码
// 虽然每次返回新实例,但由于不可变性,Compose 可以高效处理
val baseModifier = Modifier.fillMaxSize()
val paddedModifier = baseModifier.padding(16.dp)  // 新实例,但不影响 baseModifier
val finalModifier = paddedModifier.background(Color.Red)  // 又一新实例

新的实例修改,不会影响到其他使用相同Modifier的地方,状态隔离。

为什么用 Modifier?

  • 复用:同一个控件用不同 Modifier 变身不同样式。
  • 链式调用:易读。
kotlin 复制代码
@Composable
fun ModifierExample() {
    Text(
        text = "Modified Text",
        modifier = Modifier
            .padding(16.dp) // 外边距
            .background(Color.Yellow) // 背景色
            .clickable { /* 点击事件 */ } // 可点击
    )
}

这里,Text 有了 padding、黄背景和点击功能。
大重点:顺序重要!Modifier 按调用顺序应用,这一点跟XML不同。大家可以尝试一下,理解更深刻。

kotlin 复制代码
Modifier.padding(16.dp).background(Color.Red) // 先 padding 再背景:背景在内层
Modifier.background(Color.Red).padding(16.dp) // 先背景再 padding:背景在外层

四、State,最难理解的概念之一

1、一句话概括:

UI 是由状态(State)驱动生成的,而不是命令式地修改 UI

定义一个 state 变量,当它变化时,Compose会找出哪些 Composable 读取了这个 state ,Compose会 自动重组(Recompose)这些UI,且只会执行这些函数。 举个最简单的例子:

kotlin 复制代码
@Composable
fun Counter() {
    // remember 保存状态,使其在重组后依然保留
    var count by remember { mutableStateOf(0) }

    Column(
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text("当前计数:$count")

        Button(onClick = { count++ }) {
            Text("点击 +1")
        }
    }
}

解析:

  1. count 是一个状态(State<Int>
  2. Button 点击时,count 变了。
  3. Compose 检测到 count 改变后,会 自动重组(recompose) 相关 UI。
  4. Text("当前计数:$count") 会自动显示新值。

2、核心相关工具总结(重要):

工具 作用 适用场景
remember { mutableStateOf() } 创建并记住可变状态 组件内部状态(如计数器)
rememberSaveable { ... } 状态可在重建时恢复(如旋转屏幕) 屏幕旋转、进程重启
derivedStateOf { ... } 派生状态,避免重复计算 从其他状态计算出新值
StateFlow / LiveData 与 ViewModel 交互 ViewModel 层统一状态管理
collectAsState() 将 Flow 转为 Compose 的 State 响应式数据流结合 Compose

在 Compose 中,State 是 UI 的数据源,UI 是 State 的函数。State 变 → UI 自动更新。

当 State 改变时,Compose 自动重组 UI,开发者只需声明「UI 应该长什么样」。

3、State的简化

借助Kotlin 委托语法by可以进行一步简化

kotlin 复制代码
//用等号时,count的类型是MutableState<Int>,而用by后,count的类型就变成了Int
//使用by关键字替代了之前的等号,用委托的方式来为count变量赋值
val count by remember { mutableIntStateOf(0) }
var count1 = remember { mutableIntStateOf(0) }
//赋值方式
Button(onClick = { count++ }) {
    Text(text = "click me")
}
Button(onClick = { count1.intValue++ }) {
    Text(text = "click me")
}

五、remember到底记住的是什么?

Compose 是声明式的,每次重组(recomposition)都会重新执行 Composable 函数。为了避免每次都重算昂贵的东西,我们用 remember "记住"值。

1、 用 remember { mutableStateOf(initialValue) } 管理 UI 状态。

代码实例:

kotlin 复制代码
@Composable
fun Counter() {
    val count = remember { mutableIntStateOf(0) } // 记住 count,不会每次重组重置
    Column {
        Text("Count: ${count.intValue}")
        Button(onClick = { count.intValue++ }) { Text("Increment") }
    }
}

没有 remember,count 每次点击都会重置为 0。remember和mutableStateOf在Composable函数中几乎是绑定出现的。

新手在学习State时,常犯的一些概念错误

误区 解释
❌ 把局部变量当状态 普通变量不会触发重组
❌ 在重组中创建状态 每次重组都会新建,状态丢失
✅ 正确:用 remember 包裹状态 状态在重组中被"记住"

2、带键的 remember:

remember 在同一Composable 实例中记住值,重组时恢复。只有当键(key)变化时才重算。

kotlin 复制代码
val result = remember(key1, key2) { expensiveCalculation() }

键是用户 ID 时,用户变了就重算。 remember 防止不必要的计算,确保状态持久。别忘了,状态提升(hoist)到父组件能更好地管理。

3、新手常犯的错误:

remember 的作用域是 "Composable 调用位置"

❌ 先看错误代码:

kotlin 复制代码
// 错误!每次重组都会重置 
@Composable fun Simple() {
    Column { 
        var count by remember { mutableStateOf(0) } 
        Text("$count") 
    }
}

✅ 正确代码:

kotlin 复制代码
@Composable fun Simple() {
//remember 必须在 此调用位置,不能在 Column 内
   var count by remember { mutableStateOf(0) } 
   Column { 
       Text("$count") 
   }
}

六、ViewModel集成

ViewModel 是 Android 的生命周期感知组件,适合存 UI 状态和业务逻辑。Compose 完美集成 ViewModel,用 viewModel() 获取实例。

1、基本集成

build.gradle 添加依赖:

kotlin 复制代码
dependencies {  
    implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2"  
    implementation "androidx.compose.runtime:runtime-livedata:1.5.1"  
}

2、实际使用

ViewModel 存状态,Composable 观察它。

代码实例:ViewModel 类,这是搭配Flow进行使用

kotlin 复制代码
class MyViewModel : ViewModel() { 
    private val _count = MutableStateFlow(0) 
    val count = _count.asStateFlow()
    fun increment() { 
        _count.value++ 
    } 
}

这时候有小伙伴问了,我喜欢用LiveData咋办?别急,也有对应方案。入戏:

kotlin 复制代码
class MyViewModel : ViewModel() { 
    private val _count = MutableLiveData(0) 
    val count = _count
    fun increment() { 
        _count.value = _count.value!! + 1
    } 
}

Composable 使用:

kotlin 复制代码
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.lifecycle.viewmodel.compose.viewModel

@Composable
fun ViewModelCounter() {
    val viewModel: MyViewModel = viewModel() // 获取 ViewModel
    val count1 by viewModel.count.observeAsState(0) //livedata 相关
    val count = viewModel.count.collectAsState().value // 观察状态

    Button(onClick = { viewModel.increment() }) {
        Text("Count: $count")
    }
}

ViewModel 存活于配置变化(如旋转屏幕),状态不丢。

高级:用 Hilt 或 Koin 注入 ViewModel,实现依赖注入。

这让 Compose 和 MVVM 架构无缝结合。

七、单向数据流:

Compose 推崇 单向数据流(Unidirectional Data Flow, UDF)

vbnet 复制代码
State(数据源) 
   ↓
Composable(UI渲染)
   ↓
Event(用户交互)
   ↓
更新 State

比如:

kotlin 复制代码
@Composable
fun CountScreen(viewModel: CountViewModel = viewModel()) {
    val count by viewModel.count.collectAsState()

    Count(
        count = count,
        onIncrement = { viewModel.increment() }
    )
}

这样可以保证:

  • 数据变化只从上往下流;
  • 没有"UI → UI"之间的状态混乱;
  • 方便调试、测试和状态恢复。

六、列表控件性能优化

列表用 LazyColumn 或 LazyRow,只渲染可见项,适合长列表。优化关键:用 key 标识项,避免重组;用 remember 缓存子项状态。

1、基本 LazyColumn

代码实例:

kotlin 复制代码
import androidx.compose.foundation.lazy.LazyColumn 
import androidx.compose.foundation.lazy.items 
import androidx.compose.material3.Text 
import androidx.compose.runtime.Composable

@Composable fun SimpleList(items: List<String>) {
    LazyColumn { 
        items(items) {
                item -> Text(item) 
            } 
        } 
 }

2、优化:加上key

如果列表项可变,用 key 告诉 Compose 项的身份。

javascript 复制代码
//假设 item 有 id 属性。
items(items, key = { it.id }) { item -> /* ... */ }

原因:

  • 没有 key → 插入/删除项时,Compose 无法识别"哪项变了" → 所有项重绘
  • 有 key → 只重绘变化的项 → 性能提升 + 动画流畅

3、更多优化

  • 用 contentType 分组项类型,提高回收效率。
  • 避免在项里用 remember,如果需要,用 remember(item.id) {}。
  • 对于复杂项,用 LazyRow 嵌套实现网格。
    代码实例:带 key 的列表
kotlin 复制代码
data class Item(val id: Int, val name: String)

@Composable
fun OptimizedList(items: List<Item>) {
    LazyColumn {
        items(
            items = items,
            key = { it.id },
            contentType = { "text" } // 所有项同类型
        ) { item ->
            Text(item.name)
        }
    }
}

这在插入/删除项时性能更好。 记住:Lazy 组件是性能王牌,别用普通 Column 做长列表。 更加真实和复杂的列表的用法,请参见我这篇文章juejin.cn/post/756423...

八、Side Effect(世界不光只有UI,还有。。。)

这个概念更抽象,篇幅有限,我尽量用最少的语言说明白,这边只简单入门讲讲。\

1、什么是副作用

先得搞明白概念,首先声明不是吃药那种副作用哈。在 Composable 函数之外,会对外部系统或全局状态产生影响的操作即为副作用。非"UI声明"本身,但又跟Compose的生命周期息息相关。例如:

  • 网络请求(加载数据)。
  • 数据库操作(保存或读取数据)。
  • 动画或定时器启动。
  • 注册监听及回调。
  • 启动协程收集Flow
  • 日志记录或输出。

2、为什么需要它

Compose 的核心设计是声明式和响应式的:UI 是通过可组合函数(Composables)描述的纯函数,状态变化时会多次进行重组,而副作用往往是一次性或带状态行为,且在Compose函数不能直接调用副作用代码,否则会带来如下问题:

  • 每次重组都执行;

  • 重复注册;

  • 内存泄漏;

  • 状态混乱。

3、常用Side Effect API 总览

函数 执行时机 典型用途
SideEffect 每次成功重组后执行 调试、日志、同步非Compose状态
LaunchedEffect 第一次进入或 key 改变时执行 取消旧协程,启动新协程、副作用逻辑(网络、动画)
DisposableEffect 进入时执行 + 离开时清理 注册监听器、释放资源
rememberCoroutineScope 获取当前作用域启动协程 手动控制协程生命周期
produceState 从非 Compose 数据源生成状态 将 Flow / 回调转为 State
derivedStateOf 从其他 State 派生计算值 避免重复计算

4、使用范例

SideEffect:

  • 调试
  • 同步 Compose 状态到外部系统(例如 ViewModel、SharedPreference)
kotlin 复制代码
@Composable
fun LogExample(count: Int) {
    SideEffect {
        println("当前计数:$count") // 每次 count 变化并重组后执行
    }
    Text("Count = $count")
}

LaunchedEffect:最常用的副作用函数。

在 Composable 第一次进入 Composition 时启动协程,或当 key 变化时重新执行

  • key 变化会重启副作用;
  • 离开 Composition 时会自动取消协程;
  • 不会因为重组重复执行。
  • LaunchedEffect(true)是错误用法,每次重组都会执行
koltin 复制代码
@Composable
fun TimerExample() {
    var seconds by remember { mutableStateOf(0) }

    // 启动协程副作用,使用Unit当key,只会执行一次,因为Key不会变化,适用于初始化操作
    LaunchedEffect(Unit) {
        while (true) {
            delay(1000)
            seconds++
        }
    }

    Text("已过去 $seconds 秒")
}

DisposableEffect:用于需要清理的副作用(进入时执行 + 离开时清理),典型场景如下:

  • 广播接收器注册;
  • 注册监听;
  • 观察者模式;
  • 清理资源。
kotlin 复制代码
@Composable
fun SensorListenerExample(sensorManager: SensorManager) {
    DisposableEffect(Unit) {
        val listener = SensorEventListener { /* ... */ }
        sensorManager.registerListener(listener)

        //离开时自动执行
        onDispose {
            //取消传感器的监听
            sensorManager.unregisterListener(listener)
        }
    }
}

rememberCoroutineScope: 获取当前 Composable 作用域,手动启动协程(不会自动重启或取消)

  • 注意与LaunchedEffect的区别,LaunchedEffect 自动控制生命周期
kotlin 复制代码
@Composable
fun ButtonExample() {
    val scope = rememberCoroutineScope()

    Button(onClick = {
        scope.launch {
            delay(1000)
            println("按钮点击1秒后执行")
        }
    }) {
        Text("点击")
    }
}

produceState:将 非 Compose 数据源(如 Flow、回调) 转换为 Compose State,让 UI 响应变化。

  • 异步请求;
  • Flow、LiveData 转换为 State;
  • 与非 Compose 数据交互。
kotlin 复制代码
@Composable
fun UserProfile(userId: String) {
    val user by produceState<User?>(initialValue = null, userId) {
        value = fetchUser(userId) // 在协程中执行网络请求
    }
    if (user == null) {
        ircularProgressIndicator()
    }
    else {
        Text("用户名:${user!!.name}")
    }
        
}

derivedStateOf:从现有的 State 计算派生出新值,避免重复重组

  • 缓存计算;
  • 避免每次重组都重新计算。
kotlin 复制代码
val list = remember { mutableStateOf(listOf(1, 2, 3)) }
val sum by derivedStateOf { list.value.sum() }

这个派生是不是一脸懵。别急,我将大白话介绍下。 某个状态A是由多个State计算出来的,多数情况下这个状态A不会变化。如果直接监听这些State将会导致Compose频繁重组,如果通过derivedStateOf生成派生状态A,只有这个状态A发生变化才会进行重组,这样就提高了性能。

概念 含义
来源 从一个或多个 State 推导出新值
核心功能 缓存计算结果,减少不必要重组
触发时机 依赖的 State 变化时自动更新
常见用途 过滤、排序、滚动状态判断、计算属性
关键点 一定要结合 remember 使用

Side Effect错误写法

错误写法 问题
在 Composable 顶层直接 launch {} ❌ 每次重组都会重新启动
remember 外创建协程 ❌ 无法管理生命周期
忘记 onDispose 清理监听 ❌ 内存泄漏风险
不使用 key 导致 LaunchedEffect 永远不更新 ❌ 副作用不响应新参数

一句话概括:Compose 的 Side Effect 是用来安全地执行非 UI 逻辑的机制。

它确保副作用与 Composable 的生命周期绑定,不会因为重组而重复执行或泄漏

九、Navigation 跳转

Compose Navigation 管理屏幕跳转,默认情况下是在同一个Activity内进行跳转,像单页应用。它本质上是Compose函数的替换和重组。本文使用的是Material2,Material3变化较大,且在alpha阶段,感兴趣同学自己去尝试吧。

基本集成:

Groovy 复制代码
implementation "androidx.navigation:navigation-compose:2.x.x"

用 NavController 导航。 代码实例:基本设置 在 Activity:

kotlin 复制代码
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            val navController = rememberNavController()
            NavHost(navController = navController, startDestination = "home") {
                composable("home") { HomeScreen(navController) }
                composable("detail/{id}") { backStackEntry ->
                    DetailScreen(id = backStackEntry.arguments?.getString("id"))
                }
            }
        }
    }
}

HomeScreen:

kotlin 复制代码
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.navigation.NavController

@Composable
fun HomeScreen(navController: NavController) {
    Button(onClick = { navController.navigate("detail/123") }) {
        Text("Go to Detail")
    }
}

DetailScreen:

kotlin 复制代码
@Composable
fun DetailScreen(id: String?) {
    Text("Detail for ID: $id")
}

支持参数、深链、动画过渡(用 AnimatedNavHost)。 Navigation 让 app 路由简单明了。 详细说明:\

  1. Compose Navigation 的工作原理
    • NavController 管理的是 Compose UI 层面的导航
    • 页面切换实际上是 Composable 函数的替换和重组
    • 所有操作都在当前 ComponentActivity 的 setContent 范围内进行
  2. 优势
    • 性能更好,避免了 Activity 启动的开销
    • 状态管理更方便
    • 转场动画更流畅
  3. 如果确实需要启动新的 Activity,需要使用传统的 Intent 方式.
kotlin 复制代码
val intent = Intent(context, NewActivity::class.java)
context.startActivity(intent)

十、动画

Compose 的动画 API 简单强大,用 animate*AsState 动画值变化,或 AnimatedVisibility 控制可见性。

1、基本动画:值变化

代码示例:按钮大小动画,点击时按钮平滑变大/小。

kotlin 复制代码
@Composable
fun SizeAnimation() {
    val expanded = remember { mutableStateOf(false) }
    Button(
        onClick = { expanded.value = !expanded.value },
        modifier = Modifier
            .size(if (expanded.value) 200.dp else 100.dp)
            .animateContentSize(animationSpec = tween(500)) // 500ms 动画
    ) {
        Text("Toggle Size")
    }
}

2、可见性动画

AnimatedVisibility:淡入淡出等。

代码示例:

scss 复制代码
@Composable
fun VisibilityAnimation() {
    val visible = remember { mutableStateOf(true) }
    Column {
        Button(onClick = { visible.value = !visible.value }) { Text("Toggle") }
        AnimatedVisibility(
            visible = visible.value,
            enter = fadeIn(),
            exit = fadeOut()
        ) {
            Text("I appear/disappear!")
        }
    }
}

十一、额外知识点:

1、状态提升

在 Jetpack Compose 中,更推荐无状态 (stateless) 的 Compose 函数。让 UI 组件专注于展示逻辑,而状态管理交给上层组件处理。

kotlin 复制代码
// 推荐:无状态组件
@Composable
fun Counter(
    count: Int,
    onIncrement: () -> Unit,
    onDecrement: () -> Unit
) {
    Row {
        Button(onClick = onDecrement) { Text("-") }
        Text("$count")
        Button(onClick = onIncrement) { Text("+") }
    }
}

2、State和Compose的相互转化

State → Compose

我们通常不把状态(State)放在Composable里,更多的是放在Viewmodel中。 具体相关请参照本文Viewmodel标题相关部分,此处不再阐述。 反向:Compose State → Flow / LiveData

有时候你想把 Compose 的状态暴露给其他层(例如逻辑层、存储层)

scss 复制代码
val text by remember { mutableStateOf("") }

LaunchedEffect(Unit) {
    snapshotFlow { text }  // State → Flow
        .filter { it.isNotEmpty() }
        .collect { println("用户输入:$it") }
}

SnapshotFlow 会在 text 改变时发出新的流值。

十二、写在最后

Compose 不是取代XML,而是进化。学会 State + Modifier + Side Effect,你就掌握了 80% 的精髓。剩下的,边用边学吧!

相关推荐
敲上瘾几秒前
MySQL主从集群解析:从原理到Docker实战部署
android·数据库·分布式·mysql·docker·数据库架构
Jomurphys13 分钟前
测试 - 单元测试(JUnit)
android·junit·单元测试
fatiaozhang952725 分钟前
中国移动中兴云电脑W132D-RK3528-2+32G_安卓9_ADB开启线刷包
android·adb·电脑·电视盒子·刷机固件·机顶盒刷机·中兴云电脑w132d
selt79110 小时前
Redisson之RedissonLock源码完全解析
android·java·javascript
Yao_YongChao10 小时前
Android MVI处理副作用(Side Effect)
android·mvi·mvi副作用
非凡ghost11 小时前
JRiver Media Center(媒体管理软件)
android·学习·智能手机·媒体·软件需求
席卷全城11 小时前
Android 推箱子实现(引流文章)
android
齊家治國平天下12 小时前
Android 14 系统中 Tombstone 深度分析与解决指南
android·crash·系统服务·tombstone·android 14
maycho12314 小时前
MATLAB环境下基于双向长短时记忆网络的时间序列预测探索
android
思成不止于此14 小时前
【MySQL 零基础入门】MySQL 函数精讲(二):日期函数与流程控制函数篇
android·数据库·笔记·sql·学习·mysql