Android Studio
Android Studio Flamingo 版本新建项目时已经没有了Empty Compose Activity 模版
参考: https://developer.android.com/studio/releases?hl=zh-cn#updates-to-npw-nmw
参考:https://developer.android.com/build/migrate-to-catalogs
gradle 使用kotlin,2022.3.1的版本开始,默认新建项目使用kotlin gradle
参考:https://developer.android.com/build/migrate-to-kotlin-dsl
Demo 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Composable @Preview(showBackground = true) fun HelloWord () { var name = remember { mutableStateOf("HelloWord" ) } Column(modifier = Modifier.padding(10. dp)) { if (!name.value.isEmpty()) { Text( text = name.value, modifier = Modifier.fillMaxWidth()) } OutlinedTextField( value = name.value, label = { Text(text = "Name" )}, onValueChange = {name.value = it }) } }
Compose编程思想 声明式编程
命令式编程,关注过程
声明式编程,关注结果(响应式编程,观察者)
命令式编程优势: eg:一个数据对象会触发多个View模块的渲染场景,命令式编程数据的变化需要我们主动控制多个View的刷新(研发需要关注View),而声明式编程,数据和View是绑定的,数据的变化会自动驱动View的刷新。
组合和重组
如果某个参数在重组完成之前发生更改,Compose 可能会取消重组,并使用新参数开始重组。
状态管理 remember
可组合函数可以使用 remember API 将对象存储在内存中,系统会在初始组合期间将由 remember 计算的值存储在组合中,并在重组期间返回存储的值。
1 2 3 var value1 = remember { mutableStateOf("songpengfei" ) }var value2 by remember { mutableStateOf("songpengfei" ) } val (value, setValue) = remember { mutableStateOf("songpengfei" ) }
mutableStateOf返回了 MutableState,compose会观察MutableState中值的变化来进行重组。
remember 返回的对象会将对象缓存到内存中,所以在创建对象开销很大的场景中也可以使用remember来缓存。
1 var person = remember("key" ) { Person() }
当key发生变化时,person会重新创建
rememberSaveable rememberSaveable 当组件配置发生变化导致重启时,可以恢复数据。
Parcelize 对象序列化
mapSaver
listSaver
状态提升
单向数据流模式,状态下降,事件上升;
生命周期 组合中可组合项的生命周期。进入组合,执行 0 次或多次重组,然后退出组合
Column生命周期
1 2 3 4 5 6 7 8 @Composable fun MoviesScreen (movies: List <Movie >) { Column { for (movie in movies) { MovieOverview(movie) } } }
向列表尾部添加数据 向列表头部添加时,所有的MovieOverview都会重组。
1 2 3 4 5 6 7 8 9 10 @Composable fun MoviesScreen (movies: List <Movie >) { Column { for (movie in movies) { key(movie.id) { MovieOverview(movie) } } } }
Modifier
更改可组合项的大小、布局、行为和外观
添加信息,如无障碍标签
处理用户输入
添加高级互动,如使元素可点击、可滚动、可拖动或可缩放渲染
组合 :要显示什么样的界面。Compose 运行可组合函数并创建界面说明。
布局 :要放置界面的位置。该阶段包含两个步骤:测量和放置。对于布局树中的每个节点,布局元素都会根据 2D 坐标来测量并放置自己及其所有子元素。
绘制 :渲染的方式。界面元素会绘制到画布(通常是设备屏幕)中。
状态在每个阶段都可以读取,不一定非要在重组阶段。所以当postion位置发生变化时,我们可以只触发Layout和Drawing,不需要进行重组。
架构分层
此模块提供了 Compose 运行时的基本组件,例如 remember、mutableStateOf、@Composable 注释和 SideEffect。
界面层由多个模块(ui-text、ui-graphics 和 ui-tooling 等)组成。这些模块实现了界面工具包的基本组件,例如 LayoutNode、Modifier、输入处理程序、自定义布局和绘图。
此模块为 Compose 界面提供了与设计系统无关的构建块,例如 Row 和 Column、LazyColumn、特定手势的识别等。
此模块为 Compose 界面提供了 Material Design 系统的实现。
side effect LaunchedEffect 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Composable fun LaunchedEffectTest () { val state = remember { mutableStateOf("xiaomi" ) } LaunchedEffect(state) { Log.e("LaunchedEffectTest" , "request" ) delay(3000 ) state.value = "oppo" } Log.e("LaunchedEffectTest" , state.value) Column(modifier = Modifier.padding(10. dp)) { Spacer(modifier = Modifier.padding(top = 50. dp)) Button(onClick = { state.value = "vivo" }) { Text(text = "按钮" ) } Spacer(modifier = Modifier.padding(top = 100. dp)) Text(text = "手机品牌 ${state.value}" ) } }
当 LaunchedEffect 进入组合时,它会启动一个协程,并将代码块作为参数传递。如果 LaunchedEffect 退出组合,协程将取消。state值发生变化时,LaunchedEffect会发生重启
rememberCoroutineScope 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Composable fun MoviesScreen () { val scope = rememberCoroutineScope() Column { Button( onClick = { scope.launch { Log.d("MoviesScreen" ,"scope.launch" ) } }) { Text("Press me" ) } } }
可组合项外启动协程,同时rememberCoroutineScope内部会自己维护生命周期,当composable退出时,会自动释放CoroutineScope
rememberUpdatedState 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 @Composable fun UpdatedStateTest () { var message = remember { mutableStateOf("start" ) } Column(modifier = Modifier.padding(10. dp)) { Button( onClick = { message.value = "clicked" } ) { Text("描述信息" ) } LoadingScreen(message.value) } } @Composable fun LoadingScreen (text: String, scaffoldState: ScaffoldState = rememberScaffoldState() ) { val messageText by rememberUpdatedState (text) Log.e ("LoadingScreen" , "start" ) LaunchedEffect (true ) { Log.e("LoadingScreen" , "delay origin ${messageText}" ) delay(4000 ) Log.e("LoadingScreen" , "delay remember ${messageText}" ) scaffoldState.snackbarHostState.showSnackbar( message = "切换了方法" , actionLabel = messageText ) } Scaffold(scaffoldState = scaffoldState) { Column(modifier = Modifier.padding(it)) { Text(text = messageText) } } }
在LaunchedEffect 中可以感知到messageText的变化
DisposableEffect 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 @Composable fun DisposableEffectTest ( lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current ) { val inputText = remember { mutableStateOf("" ) } Log.e("DisposableEffectTest" , "Composed" ) DisposableEffect(inputText.value) { val observer = LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_START) { Log.e("DisposableEffectTest" , "ON_START" ) } else if (event == Lifecycle.Event.ON_STOP) { Log.e("DisposableEffectTest" , "ON_STOP" ) } } lifecycleOwner.lifecycle.addObserver(observer) onDispose { Log.e("DisposableEffectTest" , "onDispose" ) lifecycleOwner.lifecycle.removeObserver(observer) } } Column(modifier = Modifier.padding(10. dp)) { Button(onClick = { inputText.value = "按了一下" }) { Text(text = "按钮" + inputText.value) } } }
dispose时机:
当value发生变化时触发 onDispose
退出组合SideEffect 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Composable fun RememberAnalytics(){ val name = remember { mutableStateOf("song") } SideEffect { Log.d("SideEffect","SideEffect") } Column { Text(name.value) Button(onClick = { name.value= name.value + "a"}) { } } }
将compose转为非compose每次重组都会触发 SideEffect
produceState 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Composable fun loadNetworkImage ( url: String, imageRepository: ImageRepository ) : State<Result<ImageBitmap>> { Log.e("ProduceStateExample" , "loadNetworkImage: invoke" ) return produceState<Result<ImageBitmap>>(initialValue = Result.Loading,url, imageRepository) { val image = imageRepository.loadNetworkImage(url) value = if (image == null ) { Result.Error } else { Result.Success(image) } } } sealed class Result <T >() { object Loading : Result<ImageBitmap>() object Error : Result<ImageBitmap>() data class Success (val image: ImageBitmap) : Result<ImageBitmap>() }
将非compose转为compose
derivedStateOf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 @Composable fun TodoList (highPriorityKeywords: List<String> = listOf("Review" , "Unblock" , "Compose" ) ) { val todoTasks = remember { mutableStateListOf<String>("huawei" , "xiaomi" , "oppo" , "apple" , "Compose" ) } val highPriorityTasks by remember (highPriorityKeywords) { derivedStateOf { todoTasks.filter { highPriorityKeywords.contains(it) } } } Log.e("TodoList" , "todoTasks:${todoTasks.toList().toString()}" ) Log.e("TodoList" , "highPriorityTasks:${highPriorityTasks.toList().toString()}" ) Column(modifier = Modifier.fillMaxWidth()) { LazyColumn { item { Text(text = "add-TodoTasks" , Modifier.clickable { todoTasks.add("Review" ) }) } item { Divider( color = Color.Red, modifier = Modifier .height(2. dp) .fillMaxWidth() ) } items(highPriorityTasks) { Text(text = it) } item { Divider( color = Color.Red, modifier = Modifier .height(2. dp) .fillMaxWidth() ) } items(todoTasks) { Text(text = it) } } } }