Android ComposeUI

State

State, MutableState

compose 中, 系统需要监听值的变化, 进行自动重组, 普通对象无法监听变化, 需要使用 State, MutableState

val count = mutableIntStateOf(1)
remember

compose 重组时, 由于 state 声明在函数体中, 会被重新初始化, 为了保证值不变, 加上 remember {}, 保证在重组时, 值不变

val count = remember { mutableIntStateOf(0) }

由于直接用 remember{} 获取的是 State 对象, 使用其 value 值时不方便; 借助 by 委托语法, 直接赋值, 方便使用

var count2 by remember { mutableStateOf(10) }
rememberSaveable

remember 解决了重组过程中的值重新初始化的问题, 但是当 Activity 横竖屏切换时, 依旧会重新初始化; Compose 内部使用 rememberSaveable 来解决

val count3 by rememberSaveable { mutableIntStateOf(1) }

Stateless, Stateful, 状态管理

状态上提
StateHolder
viewModel
LiveData, RxJava, Flow 转 State
转换方法 依赖

LiveData.observeAsState()

androidx.compose:runtime-livedata
Flow.collectAsState()
Observable.subscribeAsState() androidx.compose: runtime-rxjava3

深层嵌套传值

创建 CompositionLocal
val CountLocalContent = compositionLocalOf(neverEqualPolicy()){  
    100  
}
声明 Provider, 提供值
CompositionLocalProvider(CountLocalContent.provides(1001 + count2)) {  
    CompositionLocalProvider(CountLocalContent.provides(1101 + count2)) {  
        CountText(count = count2)  
    }  
}
在子 Composeable 中, 获取值
@Composable  
fun CountLocalText() {  
    val countLocal = CountLocalContent.current  
  
    Text(  
        text = "CountLocalText count Text  $countLocal  "  
    )  
}

副作用

React 类似, Compose 重组, 方法体会重复执行, 但是部分操作并不需要每次都执行; 使用 Effect 去避免这种情况,

SideEffect

在重组成功时执行; Compose 会有多次重组, 但不一定每次都成功; SideEffect 在重组成功时执行

  • 重组成功时执行
  • 可能执行多次

DisposableEffect

React 中的 useEffect 类似, 在指定数据发生变化时执行; 如果传入 Unit 则在 Composeable 整个流程中, 只执行一次

DisposableEffect(vararg keys: Any?) { 
    // register(callback) 
    
    onDispose { 
        // unregister(callback) 
    } 
}

LaunchedEffect

Composeable 中启动协程; 在进入 composition 阶段执行, 在离开 composition 时, 自动取消; 也可以传入 key, 监听值变化时执行

LaunchedEffect(vararg keys: Any?) {
    // do Something async 
}

rememberCoroutineScope

LaunchedEffect 只能在 Compose 函数中执行; 如果在非 Composeable 中执行协程 (eg. Button 的点击事件中), 则可以由 rememberCoroutineScope 获取协程的 scope

@Composable fun Test() { 
    val scope = rememberCoroutineScope() 
    Button( onClick = { 
        scope.launch { // do something } 
        } ) { 
        Text("click me") 
    } 
}

rememberUpdatedState

一般和 LaunchEffectDisposableEffect 一起使用;

  1. LaunchEffect 不监听任何 state 时, 且内部使用传入 Composable 参数时
  2. Composeable 状态变化, 重组传入新值, 由于 LaunchedEffect 和生命周期绑定, 不会重建
  3. LaunchedEffect 获取传入的参数值, 永远是第一次的值
  4. 使用 rememberUpdatedState 则可以获取 Composeable 重组后的新值
    React 也有类似的问题, 当 props 变化时, 内部并不会获取新的值
@Composable  
fun CountText(count: Int) {  
    val count by rememberUpdatedState(newValue = count)  
    // 如果不使用 rememberUpdatedState, 底下LaunchedEffect始终获取的是第一次进入composition阶段的值  
    LaunchedEffect(Unit) {  
        Log.d("zy", "------ LaunchedEffect-- start $count")  
        for (i in 1..10) {  
            delay(1 * 1000L)  
            Log.d("zy", "------ LaunchedEffect- $i >>> $count")  
        }  
  
        Log.d("zy", "------ LaunchedEffect-- end $count")  
    }  
  
    Text(  
        text = "count Text  $count  "  
    )  
}

snapshotFlow

State 值的变化, 转换为 Flow 冷流
上面的 rememberUpdateState, 在 LaunchedEffect 绑定 Composeable 生命周期时, 只能获取到最新的值, snapshotFlow 可以获取到每一次值的变化

val count by rememberUpdatedState(newValue = count)  
  
LaunchedEffect(Unit) {  
    snapshotFlow { count }  
        .collect {  
            delay(1 * 100L)  
            Log.d("zy", "------ LaunchedEffect-- snapshotFlow $count")  
        }  
}

derivedStateOf

根据一个 State 派生出另一个 State;
仅在两个 State 变化状态不同步时使用, 如果两个 State 是同时变化, 就直接使用 源State 更新 UI 了, 使用 derivedStateOf 纯粹添加性能开销

@Composable  
 
fun MessageList(messages: List<Message>) {    
    Box {        
        val listState = rememberLazyListState()
        LazyColumn(state = listState) {            
            // ...        
        }        

        // 根据 listState 派生出 showButton; 
        // showButton 变化频率低, 使用 derivedStateOf,部分UI可以不用重组, 可以降低性能开销
        val showButton by remember {            
            derivedStateOf {
                listState.firstVisibleItemIndex > 0
            }        
        }        
        AnimatedVisibility(visible = showButton) {
            ScrollToTopButton()        
        }    
    }  
}

produceState

开启一个协程, 将普通数据, 转换为 State; eg. 将 Flow, LiveData 之类的数据, 转换成 State
内部有一个 awaitDispose 方法, 可以在离开 Composition 时做清理工作

val countChange = produceState(initialValue = 1) {  
    value = count + 1  
    awaitDispose {  
        // 做清理工作  
    }  
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容