Compose 最佳实践
状态管理
状态提升
// 不推荐
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Button(onClick = { count++ }) {
Text("Count: $count")
}
}
// 推荐
@Composable
fun Counter(count: Int, onCountChange: (Int) -> Unit) {
Button(onClick = { onCountChange(count + 1) }) {
Text("Count: $count")
}
}状态恢复
class MyViewModel : ViewModel() {
private val _uiState = MutableStateFlow(UiState())
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
fun handleEvent(event: UiEvent) {
viewModelScope.launch {
// 处理事件并更新状态
}
}
}
@Composable
fun MyScreen(viewModel: MyViewModel) {
val uiState by viewModel.uiState.collectAsState()
// 使用uiState渲染UI
}性能优化
重组优化
// 不推荐
@Composable
fun ExpensiveScreen() {
val items = remember { mutableStateListOf<Item>() }
LazyColumn {
items(items) { item ->
ExpensiveItem(item) // 每次重组都会重新创建
}
}
}
// 推荐
@Composable
fun OptimizedScreen() {
val items = remember { mutableStateListOf<Item>() }
LazyColumn {
items(
items = items,
key = { it.id } // 使用稳定的key
) { item ->
key(item.id) {
ExpensiveItem(item)
}
}
}
}副作用处理
@Composable
fun NetworkImage(url: String) {
var bitmap by remember { mutableStateOf<Bitmap?>(null) }
LaunchedEffect(url) {
bitmap = loadImage(url)
}
if (bitmap != null) {
Image(bitmap = bitmap!!.asImageBitmap(), contentDescription = null)
} else {
CircularProgressIndicator()
}
}架构设计
单向数据流
data class UiState(
val items: List<Item> = emptyList(),
val isLoading: Boolean = false,
val error: String? = null
)
sealed class UiEvent {
data class ItemClicked(val item: Item) : UiEvent()
object RefreshRequested : UiEvent()
}
@Composable
fun MyScreen(
uiState: UiState,
onEvent: (UiEvent) -> Unit
) {
when {
uiState.isLoading -> LoadingUI()
uiState.error != null -> ErrorUI(uiState.error)
else -> ContentUI(
items = uiState.items,
onItemClick = { item ->
onEvent(UiEvent.ItemClicked(item))
}
)
}
}组件分层
// UI层:纯展示组件
@Composable
fun ItemCard(item: Item, onClick: () -> Unit) {
Card(
modifier = Modifier.clickable(onClick = onClick)
) {
// UI展示逻辑
}
}
// Screen层:组合组件和处理状态
@Composable
fun ItemsScreen(viewModel: ItemsViewModel) {
val uiState by viewModel.uiState.collectAsState()
ItemsList(
items = uiState.items,
onItemClick = { viewModel.handleItemClick(it) }
)
}测试
可测试性设计
// 将业务逻辑抽离到ViewModel
class CalculatorViewModel : ViewModel() {
private val _result = MutableStateFlow(0)
val result: StateFlow<Int> = _result.asStateFlow()
fun add(a: Int, b: Int) {
_result.value = a + b
}
}
// 易于测试的UI组件
@Composable
fun Calculator(
result: Int,
onAdd: (Int, Int) -> Unit
) {
var a by remember { mutableStateOf(0) }
var b by remember { mutableStateOf(0) }
Column {
TextField(value = a.toString(), onValueChange = { a = it.toIntOrNull() ?: 0 })
TextField(value = b.toString(), onValueChange = { b = it.toIntOrNull() ?: 0 })
Button(onClick = { onAdd(a, b) }) {
Text("Add")
}
Text("Result: $result")
}
}主题和样式
一致性设计
@Composable
fun AppTheme(content: @Composable () -> Unit) {
MaterialTheme(
colors = AppColors,
typography = AppTypography,
shapes = AppShapes
) {
CompositionLocalProvider(
LocalSpacing provides Spacing(),
content = content
)
}
}
// 使用主题变量
@Composable
fun ThemedButton(
onClick: () -> Unit,
content: @Composable () -> Unit
) {
val colors = MaterialTheme.colors
val spacing = LocalSpacing.current
Button(
onClick = onClick,
colors = ButtonDefaults.buttonColors(
backgroundColor = colors.primary
),
modifier = Modifier.padding(spacing.medium)
) {
content()
}
}无障碍性
无障碍支持
@Composable
fun AccessibleContent() {
Column(
modifier = Modifier.semantics {
contentDescription = "主要内容区域"
}
) {
Text(
text = "标题",
modifier = Modifier.semantics {
heading()
}
)
Button(
onClick = { /* 处理点击 */ },
modifier = Modifier.semantics {
onClick(label = "点击执行操作", action = null)
}
) {
Text("操作按钮")
}
}
}与Android View互操作
AndroidView封装
@Composable
fun CustomAndroidView() {
AndroidView(
factory = { context ->
CustomView(context).apply {
// 初始化View
}
},
update = { view ->
// 更新View的属性
view.updateProperties()
}
)
}自定义布局
@Composable
fun CustomLayout(
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
Layout(
content = content,
modifier = modifier
) { measurables, constraints ->
// 测量子项
val placeables = measurables.map { measurable ->
measurable.measure(constraints)
}
// 确定布局大小
val width = placeables.maxOf { it.width }
val height = placeables.sumOf { it.height }
// 放置子项
layout(width, height) {
var y = 0
placeables.forEach { placeable ->
placeable.placeRelative(x = 0, y = y)
y += placeable.height
}
}
}
}复杂手势处理
@Composable
fun GestureHandler() {
var offset by remember { mutableStateOf(Offset.Zero) }
val state = rememberTransformableState { zoomChange, panChange, rotationChange ->
// 处理缩放、平移和旋转
}
Box(
modifier = Modifier
.fillMaxSize()
.transformable(state = state)
.pointerInput(Unit) {
detectDragGestures { change, dragAmount ->
change.consume()
offset += dragAmount
}
}
) {
// 内容
}
}