我使用偏好数据存储来持久化一个简单的CategoriesItemResponse
集合(我知道这不是一个好的实践,但这不是重点)。我在UserPreferencesRepository
中有一个Flow<Set<CategoriesItemResponse>>
,它通过各种ViewModel公开以供观察。
此外,我还提供了在Set中添加、删除和重新排序类别项的方法,以便每个操作都反映在流中。添加/删除功能似乎工作正常,观察流的屏幕确实对数据的变化做出了即时React。尽管重新排序逻辑的流程似乎有缺陷。
这是我的repository:
class UserPreferencesRepository(private val context: Context) {
val userFavoriteCategories = context.dataStore.data.map { prefs ->
val categoriesJson = prefs[PreferenceKeys.userFavoriteCategories]
println("categories flow read JSON in favorites flow = $categoriesJson")
categoriesJson?.convertToSetObject<CategoriesResponseItem>() ?: emptySet()
}
suspend fun addCategoryToUserFavorites(category: CategoriesResponseItem) {
withContext(Dispatchers.IO) {
try {
context.dataStore.edit { preferences ->
val categoriesJson = preferences[PreferenceKeys.userFavoriteCategories]
val categories: Set<CategoriesResponseItem> =
categoriesJson?.convertToSetObject() ?: emptySet()
val updatedCategories =
categories.toMutableSet().apply { add(category) }.toSet()
val updatedJSON = Gson().toJson(updatedCategories)
println("Updated JSON in add category: $updatedJSON")
preferences[PreferenceKeys.userFavoriteCategories] = updatedJSON
}
} catch (e: Exception) {
e.printStackTrace()
Log.e(
UserPreferencesRepository::class.java.simpleName,
"Failed to add category $category to user preferences!"
)
}
}
}
suspend fun removeCategoryFromUserFavorites(category: CategoriesResponseItem) {
withContext(Dispatchers.IO) {
try {
context.dataStore.edit { preferences ->
val categoriesJson = preferences[PreferenceKeys.userFavoriteCategories]
val categories: Set<CategoriesResponseItem> =
categoriesJson?.convertToSetObject() ?: emptySet()
val updatedCategories =
categories.toMutableSet().apply { remove(category) }.toSet()
val updatedJSON = Gson().toJson(updatedCategories)
println("updated JSON in remove category: $updatedJSON")
preferences[PreferenceKeys.userFavoriteCategories] = updatedJSON
}
} catch (e: Exception) {
e.printStackTrace()
Log.e(
UserPreferencesRepository::class.java.simpleName,
"Failed to remove category $category to user preferences!"
)
}
}
}
suspend fun reOrderCategories(from: Int, to: Int) {
withContext(Dispatchers.IO) {
try {
context.dataStore.edit { preferences ->
val categoriesJson = preferences[PreferenceKeys.userFavoriteCategories]
val categories: Set<CategoriesResponseItem> =
categoriesJson?.convertToSetObject() ?: emptySet()
val updatedCategories =
categories.toMutableList().apply {
add(to, removeAt(from))
}.toSet()
val updatedJSON = Gson().toJson(updatedCategories)
println("updated JSON in reOrder categories: $updatedJSON")
preferences[PreferenceKeys.userFavoriteCategories] = updatedJSON
}
} catch (e: Exception) {
e.printStackTrace()
Log.e(
UserPreferencesRepository::class.java.simpleName,
"Failed to persist reordering from $from to $to for user categories"
)
}
}
}
}
调用这些函数并公开流的视图模型如下所示:
class CategoriesViewModel(private val userPreferencesRepository: UserPreferencesRepository) :
ViewModel() {
//todo:sp consider getting these dynamically
private val hardcodedCategories = listOf(
CategoriesResponseItem(
id = -1,
name = "FRUITS AND VEGETABLES",
registrationType = CategoryType.PREMIUM.ordinal,
isSuggested = false
),
CategoriesResponseItem(
id = -2,
name = "NUTS, NUT PRODUCTS AND SEEDS",
registrationType = CategoryType.PREMIUM.ordinal,
isSuggested = false
),
CategoriesResponseItem(
id = -3,
name = "FRUITS AND VEGETABLES",
registrationType = CategoryType.PREMIUM.ordinal,
isSuggested = false
),
CategoriesResponseItem(
id = -4,
name = "FRUITS AND VEGETABLES",
registrationType = CategoryType.PREMIUM.ordinal,
isSuggested = false
)
)
private var backingCategories = emptyList<CategoriesResponseItem>()
val userSavedCategories = userPreferencesRepository.userFavoriteCategories.transform {
val mutableCategories = it.toMutableSet()
mutableCategories.addAll(hardcodedCategories)
backingCategories = mutableCategories.toList()
println("transform called")
emit(mutableCategories.toSet())
}
fun removeCategory(category: CategoriesResponseItem) {
viewModelScope.launch(Dispatchers.IO) {
userPreferencesRepository.removeCategoryFromUserFavorites(category)
}
}
fun onItemReorder(from: ItemPosition, to: ItemPosition) {
viewModelScope.launch(Dispatchers.IO) {
println("reorder in VM called")
userPreferencesRepository.reOrderCategories(from.index, to.index)
}
}
fun isCategoryDraggable(draggedOver: ItemPosition, dragging: ItemPosition): Boolean {
return backingCategories
.getOrNull(draggedOver.index)?.registrationType == CategoryType.FREE.ordinal
}
}
我的问题似乎是UI似乎没有对重新排序做出React,尽管调用了存储库的reorder方法并更新了首选项。transform()
用于将用户的数据与一些“高级”类别样本组合在一起,这些样本只在前端需要。Logcat似乎表明,该流程一直工作到转换lambda中的println("transform called")
,但是UI看不到项目顺序的变化,除非我重新访问页面。
下面是UI代码:
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun EditCategoriesScreen(
navController: NavController,
categoriesViewModel: CategoriesViewModel
) {
val userCategories by categoriesViewModel.userSavedCategories.collectAsState(initial = emptySet())
val reorderState = rememberReorderableLazyListState(
onMove = { from, to ->
categoriesViewModel.onItemReorder(from, to)
},
canDragOver = categoriesViewModel::isCategoryDraggable
)
Column(Modifier.fillMaxSize()) {
Column(
Modifier.weight(1f)
) {
SearchFieldAsButton(modifier = Modifier.padding(top = 25.dp)) {
navController.navigate(Screens.ADD_CATEGORIES.navRoute)
}
LazyColumn(
state = reorderState.listState,
modifier = Modifier
.padding(vertical = 16.dp)
.reorderable(reorderState)
.animateContentSize(),
verticalArrangement = Arrangement.spacedBy(15.dp),
userScrollEnabled = true,
) {
println("categories in UI = ${userCategories.toList()}")
itemsIndexed(
userCategories.toList(),
key = { _, item -> item.hashCode() }) { index, category ->
if (CategoryType.values()[category.registrationType] == CategoryType.FREE) {
ReorderableItem(
reorderableState = reorderState,
key = category.hashCode()
) {
SwipeableUnlockedCategoryItem(
modifier = Modifier
.animateItemPlacement()
.padding(horizontal = 12.dp)
.detectReorderAfterLongPress(reorderState),
displayNumber = 0,
percentageNumber = 0,
categoryName = category.name,
onDelete = {
categoriesViewModel.removeCategory(category)
}
)
}
} else {
val isComingSoonItem =
index in (userCategories.size - 2 until userCategories.size)
LockedCategoryItem(
modifier = Modifier
.animateItemPlacement()
.padding(horizontal = 12.dp),
displayNumber = 0,
percentageNumber = 0,
categoryName = category.name,
isComingSoon = isComingSoonItem
)
}
}
}
}
Column(
modifier = Modifier
.wrapContentHeight()
.background(
brush = Brush.verticalGradient(
endY = 90f,
colors = listOf(
colorResource(id = R.color.white).copy(alpha = 0.2f),
colorResource(id = R.color.white)
)
)
),
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(Modifier.height(24.dp))
Text(
modifier = Modifier
.fillMaxWidth()
.padding(start = 42.dp, end = 46.dp),
text = stringResource(id = R.string.sample_text),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.bodySmall
)
Spacer(Modifier.height(16.dp))
FoodakaiButton(
modifier = Modifier
.fillMaxWidth()
.height(50.dp)
.padding(start = 16.dp, end = 16.dp),
text = stringResource(R.string.more_insights_text),
fontSize = 18.sp
) {
navController.navigate(Screens.MORE_INSIGHTS.navRoute)
}
Spacer(Modifier.height(8.dp))
Image(
modifier = Modifier
.padding(top = 14.dp, bottom = 4.dp)
.clickable {
navController.navigate(Screens.ADD_CATEGORIES.navRoute)
},
painter = painterResource(id = R.drawable.plus_icon),
alignment = Alignment.Center,
contentDescription = stringResource(R.string.add_category_icon_content_desc)
)
Text(
text = stringResource(R.string.add_a_category_text),
style = MaterialTheme.typography.labelMedium
)
Spacer(modifier = Modifier.height(24.dp))
}
}
}
第一次访问编辑页面时,我们会得到以下日志:
UI中的类别= []
分类流读取收藏夹中的JSON流= [{“id”:12,“isSuggested”:true,“name”:“Confectionery”,“registrationType”:0},{“id”:13,“isSuggested”:true,“name”:“Meat And Meat Products(Other Than Poultry)",“registrationType”:0}]
UI中的类别= [actual-combined-list]
现在,在拖动项目并重新排序后,我们可以看到首选项被更新,但UI并没有反映重新排序,除非我们重新访问页面。以下是拖动项目后的日志:
调用了虚拟机中的重新排序
reOrder类别中更新的JSON:[{“id”:13,“isSuggested”:true,“name”:“肉类及肉制品(家禽除外)",“registrationType”:0},{“id”:12,“isSuggested”:true,“name”:“糖果”,“registrationType”:0}]〈---现在“肉类...”是第一个
分类流读取收藏夹中的JSON流= [{“id”:13,“isSuggested”:true,“name”:“肉类和肉类产品(家禽除外)",“registrationType”:0},{“id”:12,“isSuggested”:true,“name”:“糖果”,“registrationType”:0}]
转换调用
这里没有“UI中的类别”日志条目。我在哪里搞砸了?
1条答案
按热度按时间pgvzfuti1#
我猜你的问题来自
collectAsState
,因为它只会触发新的不同值的重组,这是使用相等性检查的。即使
Set
是可迭代的,并且一些实现仍然按照您创建它们的方式进行排序,但这种顺序实际上并不是Set
契约的一部分。“重新排序”的集合是相等的,因此不发出新状态。
如果项目排列很重要,
userSavedCategories
必须生成索引:aList
.