jetpack-compose

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Jetpack Compose Patterns

Jetpack Compose 模式

Modern declarative UI patterns for Android.
适用于Android的现代声明式UI模式。

State Management

状态管理

State Hoisting

状态提升

kotlin
// ✅ CORRECT: Stateless composable
@Composable
fun Counter(
    count: Int,
    onIncrement: () -> Unit,
    modifier: Modifier = Modifier
) {
    Row(modifier = modifier) {
        Text("Count: $count")
        Button(onClick = onIncrement) {
            Text("+")
        }
    }
}

// Parent owns state
@Composable
fun CounterScreen() {
    var count by rememberSaveable { mutableStateOf(0) }
    
    Counter(
        count = count,
        onIncrement = { count++ }
    )
}
kotlin
// ✅ CORRECT: Stateless composable
@Composable
fun Counter(
    count: Int,
    onIncrement: () -> Unit,
    modifier: Modifier = Modifier
) {
    Row(modifier = modifier) {
        Text("Count: $count")
        Button(onClick = onIncrement) {
            Text("+")
        }
    }
}

// Parent owns state
@Composable
fun CounterScreen() {
    var count by rememberSaveable { mutableStateOf(0) }
    
    Counter(
        count = count,
        onIncrement = { count++ }
    )
}

Remember Variants

remember的不同变体

kotlin
// remember - Survives recomposition
val alpha by remember { mutableStateOf(1f) }

// rememberSaveable - Survives config change
var count by rememberSaveable { mutableStateOf(0) }

// remember with key - Resets on key change
val animation = remember(itemId) { Animatable(0f) }

// derivedStateOf - Computed, updates only when result changes
val isValid by remember {
    derivedStateOf { email.isNotBlank() && password.length >= 8 }
}
kotlin
// remember - 可在重组后保留状态
val alpha by remember { mutableStateOf(1f) }

// rememberSaveable - 可在配置变更后保留状态
var count by rememberSaveable { mutableStateOf(0) }

// remember with key - 当key变化时重置状态
val animation = remember(itemId) { Animatable(0f) }

// derivedStateOf - 计算属性,仅当结果变化时更新
val isValid by remember {
    derivedStateOf { email.isNotBlank() && password.length >= 8 }
}

Composition Patterns

组合模式

Slot API

Slot API

kotlin
@Composable
fun AppBar(
    title: @Composable () -> Unit,
    navigationIcon: @Composable () -> Unit = {},
    actions: @Composable RowScope.() -> Unit = {}
) {
    TopAppBar(
        title = { title() },
        navigationIcon = { navigationIcon() },
        actions = actions
    )
}

// Usage
AppBar(
    title = { Text("Home") },
    navigationIcon = { IconButton(onClick = {}) { Icon(Icons.Default.Menu, null) } },
    actions = {
        IconButton(onClick = {}) { Icon(Icons.Default.Search, null) }
    }
)
kotlin
@Composable
fun AppBar(
    title: @Composable () -> Unit,
    navigationIcon: @Composable () -> Unit = {},
    actions: @Composable RowScope.() -> Unit = {}
) {
    TopAppBar(
        title = { title() },
        navigationIcon = { navigationIcon() },
        actions = actions
    )
}

// 使用示例
AppBar(
    title = { Text("Home") },
    navigationIcon = { IconButton(onClick = {}) { Icon(Icons.Default.Menu, null) } },
    actions = {
        IconButton(onClick = {}) { Icon(Icons.Default.Search, null) }
    }
)

Modifier Pattern

Modifier模式

kotlin
@Composable
fun CustomButton(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,  // First optional parameter
    enabled: Boolean = true,
    content: @Composable RowScope.() -> Unit
) {
    Button(
        onClick = onClick,
        modifier = modifier,  // Apply modifier first
        enabled = enabled,
        content = content
    )
}
kotlin
@Composable
fun CustomButton(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,  // 第一个可选参数
    enabled: Boolean = true,
    content: @Composable RowScope.() -> Unit
) {
    Button(
        onClick = onClick,
        modifier = modifier,  // 先应用modifier
        enabled = enabled,
        content = content
    )
}

Side Effects

副作用处理

LaunchedEffect

LaunchedEffect

kotlin
@Composable
fun HomeScreen(viewModel: HomeViewModel) {
    // Runs once
    LaunchedEffect(Unit) {
        viewModel.loadData()
    }
    
    // Runs when key changes
    LaunchedEffect(userId) {
        viewModel.loadUser(userId)
    }
}
kotlin
@Composable
fun HomeScreen(viewModel: HomeViewModel) {
    // 仅执行一次
    LaunchedEffect(Unit) {
        viewModel.loadData()
    }
    
    // 当key变化时执行
    LaunchedEffect(userId) {
        viewModel.loadUser(userId)
    }
}

DisposableEffect

DisposableEffect

kotlin
@Composable
fun LifecycleObserver(onResume: () -> Unit) {
    val lifecycleOwner = LocalLifecycleOwner.current
    
    DisposableEffect(lifecycleOwner) {
        val observer = LifecycleEventObserver { _, event ->
            if (event == Lifecycle.Event.ON_RESUME) onResume()
        }
        lifecycleOwner.lifecycle.addObserver(observer)
        
        onDispose {
            lifecycleOwner.lifecycle.removeObserver(observer)
        }
    }
}
kotlin
@Composable
fun LifecycleObserver(onResume: () -> Unit) {
    val lifecycleOwner = LocalLifecycleOwner.current
    
    DisposableEffect(lifecycleOwner) {
        val observer = LifecycleEventObserver { _, event ->
            if (event == Lifecycle.Event.ON_RESUME) onResume()
        }
        lifecycleOwner.lifecycle.addObserver(observer)
        
        onDispose {
            lifecycleOwner.lifecycle.removeObserver(observer)
        }
    }
}

Theming

主题配置

Material 3

Material 3

kotlin
@Composable
fun AppTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit
) {
    val colorScheme = if (darkTheme) DarkColorScheme else LightColorScheme
    
    MaterialTheme(
        colorScheme = colorScheme,
        typography = AppTypography,
        content = content
    )
}

// Usage
val backgroundColor = MaterialTheme.colorScheme.surface
val textStyle = MaterialTheme.typography.bodyLarge
kotlin
@Composable
fun AppTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit
) {
    val colorScheme = if (darkTheme) DarkColorScheme else LightColorScheme
    
    MaterialTheme(
        colorScheme = colorScheme,
        typography = AppTypography,
        content = content
    )
}

// 使用示例
val backgroundColor = MaterialTheme.colorScheme.surface
val textStyle = MaterialTheme.typography.bodyLarge

Lists

列表组件

LazyColumn

LazyColumn

kotlin
LazyColumn {
    items(
        items = users,
        key = { it.id }  // Critical for performance
    ) { user ->
        UserItem(user = user)
    }
}
kotlin
LazyColumn {
    items(
        items = users,
        key = { it.id }  // 对性能至关重要
    ) { user ->
        UserItem(user = user)
    }
}

Animations

动画实现

Animate Values

数值动画

kotlin
val alpha by animateFloatAsState(
    targetValue = if (visible) 1f else 0f,
    animationSpec = tween(durationMillis = 300)
)

val size by animateDpAsState(
    targetValue = if (expanded) 200.dp else 100.dp
)
kotlin
val alpha by animateFloatAsState(
    targetValue = if (visible) 1f else 0f,
    animationSpec = tween(durationMillis = 300)
)

val size by animateDpAsState(
    targetValue = if (expanded) 200.dp else 100.dp
)

AnimatedContent

AnimatedContent

kotlin
AnimatedContent(
    targetState = state,
    transitionSpec = {
        fadeIn() togetherWith fadeOut()
    }
) { targetState ->
    when (targetState) {
        is Loading -> LoadingContent()
        is Success -> SuccessContent(targetState.data)
        is Error -> ErrorContent()
    }
}

Remember: Compose is declarative. Describe the UI, don't command it.
kotlin
AnimatedContent(
    targetState = state,
    transitionSpec = {
        fadeIn() togetherWith fadeOut()
    }
) { targetState ->
    when (targetState) {
        is Loading -> LoadingContent()
        is Success -> SuccessContent(targetState.data)
        is Error -> ErrorContent()
    }
}

注意:Compose是声明式框架。只需描述UI,而非直接操控它。