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% 的精髓。剩下的,边用边学吧!

相关推荐
芦半山4 小时前
Looper究竟在等什么?
android
czhc11400756636 小时前
JAVA1027抽象类;抽象类继承
android·java·开发语言
_Sem7 小时前
KMP实战:从单端到跨平台的完整迁移指南
android·前端·app
從南走到北7 小时前
JAVA国际版任务悬赏发布接单系统源码支持IOS+Android+H5
android·java·ios·微信·微信小程序·小程序
vistaup7 小时前
Android ContentProvier
android·数据库
我是场7 小时前
Android Camera 从应用到硬件之- 枚举Camera - 1
android
4Forsee7 小时前
【Android】View 事件分发机制与源码解析
android·java·前端
咕噜签名分发冰淇淋7 小时前
苹果ios安卓apk应用APP文件怎么修改手机APP显示的名称
android·ios·智能手机
应用市场7 小时前
从零开始打造Android桌面Launcher应用:原理剖析与完整实现
android