Jetpack Compose 实现主页面与局部页面独立刷新的最佳实践

在 Jetpack Compose 开发中,我们经常遇到这样的需求:主页面包含局部页面,主页面刷新时需要更新局部页面,同时局部页面也需要能独立刷新。本文将介绍几种优雅的实现方案。

核心需求

  1. 主页面刷新时能触发局部页面更新
  2. 局部页面能独立刷新自身数据
  3. 避免不必要的重组,保持良好性能

方案一:状态提升 + 回调(简单场景)

实现思路

将局部页面的状态提升到主页面管理,通过回调函数实现局部刷新。

@Composable
fun MainScreen() {
    // 主页面状态
    var mainData by remember { mutableStateOf<MainData?>(null) }
    // 局部页面状态(提升到主页面)
    var partialData by remember { mutableStateOf<PartialData?>(null) }

    // 刷新主数据(同时刷新局部数据)
    fun refreshAll() {
        viewModelScope.launch {
            mainData = repository.loadMainData()
            partialData = repository.loadPartialData()
        }
    }

    // 仅刷新局部数据
    fun refreshPartial() {
        viewModelScope.launch {
            partialData = repository.loadPartialData()
        }
    }

    Column {
        // 主页面内容
        Button(onClick = { refreshAll() }) {
            Text("刷新全部数据")
        }
        
        // 局部页面组件
        PartialSection(
            data = partialData,
            onRefresh = { refreshPartial() }
        )
    }
}

@Composable
fun PartialSection(data: PartialData?, onRefresh: () -> Unit) {
    Column {
        Button(onClick = onRefresh) {
            Text("仅刷新局部数据")
        }
        Text(data?.content ?: "暂无数据")
    }
}

适用场景

  • 简单的父子组件关系
  • 局部页面逻辑不复杂
  • 不需要跨组件共享状态

方案二:ViewModel + 共享状态(推荐方案)

实现思路

使用 ViewModel 统一管理状态,提供独立的刷新方法。

class SharedViewModel : ViewModel() {
    // 主数据
    private val _mainData = mutableStateOf<MainData?>(null)
    val mainData: State<MainData?> = _mainData

    // 局部数据
    private val _partialData = mutableStateOf<PartialData?>(null)
    val partialData: State<PartialData?> = _partialData

    // 刷新主数据(同时刷新局部数据)
    fun refreshAll() {
        viewModelScope.launch {
            _mainData.value = repository.loadMainData()
            refreshPartial()
        }
    }

    // 仅刷新局部数据
    fun refreshPartial() {
        viewModelScope.launch {
            _partialData.value = repository.loadPartialData()
        }
    }
}

@Composable
fun MainScreen(viewModel: SharedViewModel = viewModel()) {
    Column {
        Button(onClick = { viewModel.refreshAll() }) {
            Text("刷新全部数据")
        }
        
        PartialSection(viewModel)
    }
}

@Composable
fun PartialSection(viewModel: SharedViewModel) {
    val partialData by viewModel.partialData.collectAsState()
    
    Column {
        Button(onClick = { viewModel.refreshPartial() }) {
            Text("仅刷新局部数据")
        }
        Text(partialData?.content ?: "暂无数据")
    }
}

优势

  • 状态管理清晰
  • 支持跨组件共享状态
  • 业务逻辑与UI分离
  • 便于单元测试

方案三:事件驱动(高级场景)

实现思路

使用事件流(Flow)实现解耦通信。

class EventViewModel : ViewModel() {
    // 数据状态
    private val _mainData = mutableStateOf<MainData?>(null)
    val mainData: State<MainData?> = _mainData
    
    private val _partialData = mutableStateOf<PartialData?>(null)
    val partialData: State<PartialData?> = _partialData

    // 刷新事件
    private val _refreshEvent = MutableSharedFlow<RefreshType>()
    val refreshEvent = _refreshEvent.asSharedFlow()

    init {
        viewModelScope.launch {
            refreshEvent.collect { type ->
                when(type) {
                    RefreshType.ALL -> {
                        _mainData.value = repository.loadMainData()
                        _partialData.value = repository.loadPartialData()
                    }
                    RefreshType.PARTIAL -> {
                        _partialData.value = repository.loadPartialData()
                    }
                }
            }
        }
    }

    fun triggerRefresh(type: RefreshType) {
        viewModelScope.launch {
            _refreshEvent.emit(type)
        }
    }
}

enum class RefreshType { ALL, PARTIAL }

@Composable
fun MainScreen(viewModel: EventViewModel = viewModel()) {
    Column {
        Button(onClick = { viewModel.triggerRefresh(RefreshType.ALL) }) {
            Text("刷新全部数据")
        }
        
        PartialSection(viewModel)
    }
}

@Composable
fun PartialSection(viewModel: EventViewModel) {
    val partialData by viewModel.partialData.collectAsState()
    
    Column {
        Button(onClick = { viewModel.triggerRefresh(RefreshType.PARTIAL) }) {
            Text("仅刷新局部数据")
        }
        Text(partialData?.content ?: "暂无数据")
    }
}

适用场景

  • 复杂的状态管理
  • 需要完全解耦的组件通信
  • 多个组件需要响应同一事件

性能优化建议

  1. 使用 remember 缓存计算结果

    val processedData = remember(partialData) {
        heavyProcessing(partialData)
    }
    
  2. 避免不必要的重组

    @Stable
    data class PartialState(val data: PartialData, val isLoading: Boolean)
    
  3. 分页加载大数据集

    val pagingData = viewModel.pagingData.collectAsLazyPagingItems()
    LazyColumn {
        items(pagingData) { item ->
            ItemView(item)
        }
    }
    

总结

方案 优点 缺点 适用场景
状态提升 简单直接 状态臃肿 简单父子组件
ViewModel 职责分离,易于测试 需要额外类 大多数场景
事件驱动 完全解耦 实现复杂 复杂状态管理

推荐选择

  • 对于大多数应用,**方案二(ViewModel)**是最佳选择
  • 简单场景可以使用方案一
  • 只有在确实需要解耦时才考虑方案三

希望本文能帮助你优雅地实现 Jetpack Compose 中的页面刷新逻辑!

你可能感兴趣的:(Compose,android,compose,android)