Android LiveData保留了该值,并允许它观察更改并尊重应用程序组件的生命周期。
使用 DataBinding 和 LiveData 更新 Recyclerview 数据项
将项目添加到回收站视图并处理增量更新 我已经有了一个可行的解决方案。
我在 ViewModel 中创建函数以避免在使用 Flow 和 LiveData 时重复。在示例中,我创建了一个扩展函数,如下所示。在和我的朋友交谈时,我们开始考虑
使用 MutableLiveData 和具有多个图像的 LiveData 实现 ViewModel
下面的代码使我能够从手机图片库中最多选择 4 张图片。但是,我需要有关视图模型代码的帮助。如果选择一张图像并且手机配置发生变化,则图像
Kotlin - 在运行函数之前等待多个 LiveData 被观察
我正在使用 viewModel 从房间数据库中提取实时数据。我有 2 个从我的 viewModel 中提取的 LiveData,然后我将运行一个函数从我的服务器中提取数据。我需要两个值都...
Flow<Set<CategoriesItemResponse>> 重新排序内容后似乎没有发出新的转换值
我正在使用首选项 DataStore 来保存一组简单的 CategoriesItemResponse(我知道这不是一个好的做法,但这不是重点)。我有流量 我正在使用首选项数据存储来保存一组简单的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 中的类别”日志条目。我哪里搞砸了? 我猜你的问题来自collectAsState,因为它只会触发对使用相等性检查的新的不同值的重组。 尽管 Sets 是可迭代的,并且某些实现确实保留了您使用该顺序创建它们的顺序,但实际上并不是 Set 合同的一部分。 “重新排序”的集合是相等的,因此不会发出新状态。 如果项目安排很重要userSavedCategories必须产生一些索引:aList.
在 Viewmodel 中使用 LiveData,我们使用 switchMap 或 Transformations.map 像这样 val recipesList = cuisineType.switchMap { repository.getDisplayRecipes(it.cuisineType).asLiveData() } 什么...
我正在构建一个 android 应用程序,它有多个共享数据模型的活动。 数据模型由两层组成: 第一个存储要使用的对象列表,以及用于...的方法
我应该如何在自定义视图中观察 LiveData。我试图将它的上下文转换为 lifecycleOwner 但它会产生一些问题并且在所有情况下都不起作用。 我试着放一个二传手,但没用
为什么要使用 Transformations.switchMap?
像这样使用 Transformations.switchMap 有什么优势吗 MutableLiveData userId = ...; LiveData user = Transformations.switchMap(userIdLiveData, id -> repos...
需要将api返回的数据添加到List,但只能post到LiveData?
从我的片段中调用:videoViewModel.fetchContentSections(); 从我的虚拟机中调用:public void fetchContentSections(){repository.getContent();} 从我的回购协议我这样做: apiService.getContent(请求).
我尝试从 viewModelScope 为 liveFata 赋值,但是当我在片段中检查该值时,它为空。我想,viewModel 的实例有问题,但我找不到解决方案。请,任何
最近开始学习Android,遇到了一个问题。我在项目中使用了MVVM、Coroutines、Live Data、Dagger 2、Retrofit。问题是数据没有显示在Fragment中
我有一个房间数据库,其中有与艺术家相关的歌曲,当我从 Main Activity 溢出菜单更改艺术家时,带有显示歌曲列表的 recyclerview 的片段不会...
这是视图模型 有趣的 getUsers() = userRepository.getUsers() 这是存储库 有趣的 getUsers(): LiveData>> = liveData { 发出(结果。加载) ...
如何用初始值初始化 MutableLiveData? 我正在寻找类似的东西: val text = MutableLiveData("初始值")
我有一个在我的应用程序启动时正在运行的房间查询。 @Query("SELECT * FROM myTable") 有趣的 get(): LiveData?> 查询运行后,我的用户将选择几个
我想将存储库响应作为祝酒词传播给用户,所以我将实时数据侦听器放在我的活动中的后期创建方法中 mainViewModel.toast.observe(这个){ Log.d(TAG, "初始化...
我有以下测试,检查活动是否通过视图模型正确地从存储库获取数据。 @Config(应用程序 = TestApplication::类) @RunWith(RobolectricTestRunner::类) @LooperMode(
savedStateHandle 不从 livedata 恢复数据
我有一个带有 1 个按钮的自定义对话框片段。当这个按钮什么都不做时,我有 1 个案例。 当用户单击按钮时,我调用此方法来保存对话框中的数据: 有趣的片段。
LiveData 在 Jetpack Compose Android 上返回 null
道 @Query("SELECT COUNT(category) FROM todolist_table") 有趣的 getAllTaskCount() :LiveData 回购 暂停乐趣 getAllTaskCount() : LiveData { 返回 todoDao.