正在加载,请稍候…

Android Jetpack Compose Deep Dive: State, Animation, and Performance

A comprehensive guide to Jetpack Compose covering state management with ViewModel, complex animations, performance optimization, and interoperability with the View system.

Android Jetpack Compose Deep Dive: State, Animation, and Performance

Jetpack Compose has become the standard for Android UI development. With Compose BOM 2026.x, stability is excellent and the API surface is rich. This guide explores advanced patterns for state management, animations, performance optimization, and View system integration.

Understanding Composition and State

State Hoisting Pattern

// Stateless component - easy to test and reuse
@Composable
fun EmailInput(
    value: String,
    onValueChange: (String) -> Unit,
    isError: Boolean = false,
    modifier: Modifier = Modifier,
) {
    OutlinedTextField(
        value = value,
        onValueChange = onValueChange,
        label = { Text("Email") },
        isError = isError,
        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email),
        modifier = modifier,
    )
}

ViewModel with StateFlow

@HiltViewModel
class LoginViewModel @Inject constructor(
    private val authRepository: AuthRepository,
) : ViewModel() {

    private val _uiState = MutableStateFlow(LoginUiState())
    val uiState: StateFlow<LoginUiState> = _uiState.asStateFlow()

    fun login() {
        viewModelScope.launch {
            _uiState.update { it.copy(isLoading = true) }
            authRepository.login(_uiState.value.email, _uiState.value.password)
                .onSuccess { _uiState.update { it.copy(isLoading = false, loginSuccess = true) } }
                .onFailure { e -> _uiState.update { it.copy(isLoading = false, errorMessage = e.message) } }
        }
    }
}

data class LoginUiState(
    val email: String = "",
    val password: String = "",
    val isLoading: Boolean = false,
    val loginSuccess: Boolean = false,
    val errorMessage: String? = null,
)

Advanced Compose Animations

AnimatedVisibility with Custom Transitions

@Composable
fun NotificationBanner(message: String?, onDismiss: () -> Unit) {
    AnimatedVisibility(
        visible = message != null,
        enter = slideInVertically(
            initialOffsetY = { -it },
            animationSpec = spring(
                dampingRatio = Spring.DampingRatioMediumBouncy,
                stiffness = Spring.StiffnessLow,
            )
        ) + fadeIn(),
        exit = slideOutVertically(targetOffsetY = { -it }) + fadeOut(),
    ) {
        Surface(
            color = MaterialTheme.colorScheme.inverseSurface,
            shape = RoundedCornerShape(12.dp),
            modifier = Modifier.fillMaxWidth().padding(16.dp),
        ) {
            Row(
                modifier = Modifier.padding(16.dp),
                verticalAlignment = Alignment.CenterVertically,
            ) {
                Text(message ?: "", modifier = Modifier.weight(1f))
                IconButton(onClick = onDismiss) {
                    Icon(Icons.Default.Close, "Dismiss")
                }
            }
        }
    }
}

Animate as State

@Composable
fun ProgressButton(progress: Float, onClick: () -> Unit, modifier: Modifier = Modifier) {
    val animatedProgress by animateFloatAsState(
        targetValue = progress,
        animationSpec = tween(durationMillis = 600, easing = FastOutSlowInEasing),
        label = "progress",
    )

    val backgroundColor by animateColorAsState(
        targetValue = if (progress >= 1f)
            MaterialTheme.colorScheme.tertiary
        else
            MaterialTheme.colorScheme.primary,
        label = "background",
    )

    Box(
        modifier = modifier
            .clip(RoundedCornerShape(50))
            .background(backgroundColor)
            .clickable(onClick = onClick)
            .padding(horizontal = 24.dp, vertical = 12.dp),
    ) {
        LinearProgressIndicator(
            progress = { animatedProgress },
            modifier = Modifier.fillMaxWidth().height(4.dp).align(Alignment.BottomCenter),
        )
        Text(
            text = if (progress >= 1f) "Done!" else "${(progress * 100).toInt()}%",
            color = Color.White,
            fontWeight = FontWeight.SemiBold,
        )
    }
}

Performance Optimization

Avoiding Unnecessary Recompositions

// Use rememberUpdatedState for callbacks
@Composable
fun OptimalExample(count: Int, onCountChanged: (Int) -> Unit) {
    val currentOnCountChanged by rememberUpdatedState(onCountChanged)
    Button(onClick = { currentOnCountChanged(count + 1) }) {
        Text("Count: $count")
    }
}

derivedStateOf for Expensive Computations

@Composable
fun SortedList(items: List<Item>) {
    val listState = rememberLazyListState()

    val sortedItems by remember(items) {
        derivedStateOf { items.sortedBy { it.priority } }
    }

    val showScrollToTop by remember {
        derivedStateOf { listState.firstVisibleItemIndex > 3 }
    }

    Box {
        LazyColumn(state = listState) {
            items(sortedItems, key = { it.id }) { item ->
                ItemRow(item = item)
            }
        }
        AnimatedVisibility(
            visible = showScrollToTop,
            modifier = Modifier.align(Alignment.BottomEnd),
        ) {
            FloatingActionButton(
                onClick = { /* scroll to top */ },
                modifier = Modifier.padding(16.dp),
            ) {
                Icon(Icons.Default.KeyboardArrowUp, "Scroll to top")
            }
        }
    }
}

Stable and Immutable Annotations

@Immutable
data class UserProfile(
    val id: String,
    val name: String,
    val avatarUrl: String,
    val bio: String,
)

@Stable
class CartState {
    var items by mutableStateListOf<CartItem>()
    val total: Double get() = items.sumOf { it.price * it.quantity }
}

Compose and View Interoperability

Embedding Compose in Fragment

class MyFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?,
    ): View {
        return ComposeView(requireContext()).apply {
            setViewCompositionStrategy(
                ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
            )
            setContent {
                MaterialTheme {
                    val viewModel: MyViewModel by viewModels()
                    MyComposableScreen(viewModel = viewModel)
                }
            }
        }
    }
}

Embedding Android View in Compose

@Composable
fun LegacyMapView(latLng: LatLng, modifier: Modifier = Modifier) {
    val mapView = rememberMapViewWithLifecycle()

    AndroidView(
        factory = { mapView },
        modifier = modifier,
        update = { view ->
            view.getMapAsync { map ->
                map.moveCamera(CameraUpdateFactory.newLatLng(latLng))
            }
        },
    )
}

Lazy Layout Optimizations

@Composable
fun OptimizedFeed(items: List<FeedItem>, onItemClick: (FeedItem) -> Unit) {
    LazyColumn(
        contentPadding = PaddingValues(16.dp),
        verticalArrangement = Arrangement.spacedBy(12.dp),
    ) {
        items(
            items = items,
            key = { item -> item.id },
            contentType = { item -> item::class.simpleName },
        ) { item ->
            FeedItemCard(
                item = item,
                onClick = { onItemClick(item) },
                modifier = Modifier.animateItem(),
            )
        }
    }
}

Compose Multiplatform

Compose Multiplatform enables sharing UI code across Android, iOS, desktop, and web:

// Shared UI code
@Composable
fun SharedProfileCard(user: User, modifier: Modifier = Modifier) {
    Card(modifier = modifier.fillMaxWidth()) {
        Row(modifier = Modifier.padding(16.dp)) {
            AsyncImage(
                model = user.avatarUrl,
                contentDescription = null,
                modifier = Modifier.size(48.dp).clip(CircleShape),
            )
            Spacer(modifier = Modifier.width(12.dp))
            Column {
                Text(user.name, style = MaterialTheme.typography.titleMedium)
                Text(user.bio, style = MaterialTheme.typography.bodySmall)
            }
        }
    }
}

Conclusion

Jetpack Compose continues to mature with each release. Mastering state hoisting, ViewModel integration, animation APIs, and performance optimization enables you to build beautiful, responsive Android apps. The interop story with legacy Views means you can incrementally adopt Compose in existing projects, while new projects benefit from the best developer experience in Android history.