Flow<Set<CategoriesItemResponse>> 重新排序内容后似乎没有发出新的转换值

问题描述 投票:0回答:1

我正在使用首选项数据存储来保存一组简单的

CategoriesItemResponse
(我知道这不是一个好的做法,但这不是重点)。我的
Flow<Set<CategoriesItemResponse>>
中有一个
UserPreferencesRepository
,它通过各种 ViewModels 公开以供观察。

此外,我还提供了在 Set 中添加、删除和重新排序类别项的方法,以便每个操作都反映在流中。添加/删除功能似乎工作正常,观察流程的屏幕确实会立即对数据的变化做出反应。不过,重新排序逻辑的流程似乎有缺陷。

这是我的存储库:

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"
                )
            }
        }
    }
}

调用这些函数同时暴露流程的viewmodel是这样的:

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 似乎没有对重新排序做出反应,尽管调用了存储库的重新排序方法并且更新了首选项。

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 中的类别 = []

category flow read JSON in favorites flow = [{"id":12,"isSuggested":true,"name":"糖果","registrationType":0},{"id":13,"isSuggested":true,"name":"肉 和肉类产品(家禽除外)","registrationType":0}]

UI 中的类别 = [实际组合列表]

现在拖动项目并重新排序后,我们可以看到首选项已更新,但 UI 不会反映重新排序,除非我们重新访问页面。以下是拖动项目后的日志:

在 VM 中重新排序

更新了 reOrder 类别中的 JSON: [{"id":13,"isSuggested":true,"name":"肉类和肉类产品(其他 比 家禽)","registrationType":0},{"id":12,"isSuggested":true,"name":"Confectionery","registrationType":0}] <--- now "Meat..." is first

category flow read JSON in favorites flow = [{"id":13,"isSuggested":true,"name":"肉类和肉类产品(其他 比 家禽)","registrationType":0},{"id":12,"isSuggested":true,"name":"Confectionery","registrationType":0}]

转换称为

这里没有“UI 中的类别”日志条目。我哪里搞砸了?

android kotlin android-jetpack-compose android-livedata
1个回答
0
投票

我猜你的问题来自

collectAsState
,因为它只会触发对使用相等性检查的新的不同值的重组。

尽管

Set
s 是可迭代的,并且某些实现确实保留了您使用该顺序创建它们的顺序,但实际上并不是
Set
合同的一部分。 “重新排序”的集合是相等的,因此不会发出新状态。

如果项目安排很重要

userSavedCategories
必须产生一些索引:a
List
.

© www.soinside.com 2019 - 2024. All rights reserved.