Kotlin与Jetpack Compose实现变量真正不可变及计数问题解决
问题:固定Jetpack Compose中统计值直到主动更新
核心问题
需要统计数据库中不同分组的记录数,但当前通过切换sortType复用同一个groceries流,导致后续查询会覆盖之前的计数。希望每个分组的计数能固定下来,直到主动触发重新统计。
当前代码
ViewModel代码
private val _sortType = MutableStateFlow(SortType.CHECK_TO_ADD) private val _groceries = _sortType .flatMapLatest { _sortType -> when (_sortType) { SortType.ITEM_NAME -> dao.listAllItems() SortType.CHECK_TO_ADD -> dao.listAllItemsChecked() SortType.ONLY_CHECK_TO_ADD -> dao.listShoppingList() SortType.ONLY_CHECKED_CHECKED -> dao.listPurchasedItems() } } private val _state = MutableStateFlow(GroceriesState()) val state = combine(_state, _sortType, _groceries) { state, sortType, groceries -> state.copy( groceries = groceries, sortType = sortType ) }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), GroceriesState())
DAO代码
//List purchased items @Query("SELECT * FROM grocery_table WHERE checkChecked = 1") fun listPurchasedItems() : Flow<List<Groceries>> // // QUERIES IN THE SORT ROUTINE // //List the items in shopping list - INCLUDED IN SORT @Query("SELECT * FROM grocery_table WHERE checkToAdd = 1") fun listShoppingList() : Flow<List<Groceries>> //list all grocery items - INCLUDED IN SORT @Query("SELECT * FROM grocery_table ORDER BY itemName ASC") fun listAllItems(): Flow<List<Groceries>> //list all grocery items to create grocery list and add new items to DB- INCLUDED IN SORT @Query("SELECT * FROM grocery_table ORDER BY checkToAdd ASC, itemName ASC") fun listAllItemsChecked(): Flow<List<Groceries>>
Composable中的问题代码
onEvent(GroceriesEvent.SortGroceries(SortType.ONLY_CHECKED_CHECKED)) val countPurchased: Int = state.groceries.size onEvent(GroceriesEvent.SortGroceries(SortType.ONLY_CHECK_TO_ADD)) val countSList:Int = state.groceries.size
Text( text = "Shopping List", modifier = Modifier .weight(1f) .padding(start = 40.dp), fontSize = 17.sp, fontWeight = FontWeight.SemiBold, color = md_theme_dark_onTertiaryContainer ) Text( text = "${countSList}/${countPurchased}", modifier = Modifier, fontSize = 17.sp, fontWeight = FontWeight.SemiBold, color = md_theme_dark_onTertiaryContainer )
解决方案
1. 修改UIState,添加分组计数字段
扩展GroceriesState,专门存储各分组的计数,避免复用同一个列表:
data class GroceriesState( val groceries: List<Groceries> = emptyList(), val sortType: SortType = SortType.CHECK_TO_ADD, // 新增字段存储固定计数 val purchasedCount: Int = 0, val shoppingListCount: Int = 0 )
2. ViewModel中同时监听多个查询流
不再通过切换sortType切换查询,而是同时监听所有需要统计的流,直接更新对应计数字段:
private val _sortType = MutableStateFlow(SortType.CHECK_TO_ADD) // 单独监听各个查询流 private val allItems = dao.listAllItems() private val allItemsChecked = dao.listAllItemsChecked() private val purchasedItems = dao.listPurchasedItems() private val shoppingListItems = dao.listShoppingList() private val _state = MutableStateFlow(GroceriesState()) val state = combine( _state, _sortType, allItems, allItemsChecked, purchasedItems, shoppingListItems ) { state, sortType, allItems, allCheckedItems, purchasedItems, shoppingItems -> // 保留原有列表展示逻辑 val currentGroceries = when(sortType) { SortType.ITEM_NAME -> allItems SortType.CHECK_TO_ADD -> allCheckedItems SortType.ONLY_CHECK_TO_ADD -> shoppingItems SortType.ONLY_CHECKED_CHECKED -> purchasedItems } state.copy( groceries = currentGroceries, sortType = sortType, // 固定各分组计数 purchasedCount = purchasedItems.size, shoppingListCount = shoppingItems.size ) }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), GroceriesState())
3. 更新Composable代码,直接读取固定计数
移除手动切换sortType的逻辑,直接从State中读取预存的固定值:
// 直接读取State中的固定计数 val countPurchased = state.purchasedCount val countSList = state.shoppingListCount
Text( text = "Shopping List", modifier = Modifier .weight(1f) .padding(start = 40.dp), fontSize = 17.sp, fontWeight = FontWeight.SemiBold, color = md_theme_dark_onTertiaryContainer ) Text( text = "${countSList}/${countPurchased}", modifier = Modifier, fontSize = 17.sp, fontWeight = FontWeight.SemiBold, color = md_theme_dark_onTertiaryContainer )
原理说明
- 通过ViewModel同时监听所有需要统计的数据库流,将每个分组的计数存储在UIState的独立字段中,这些字段会随数据库更新自动同步,但不会互相覆盖。
- Composable直接读取State中的计数字段,无需手动切换查询,彻底避免了值被覆盖的问题。
内容的提问来源于stack exchange,提问作者Ray
相关产品推荐
相关产品推荐

