想象一下在主要活动中向您介绍的帖子列表(例如Facebook)。基本的“单一事实来源”是数据库,Android的Room用于获取并观察此帖子列表。
这就是我观察数据的方式(由于许可证问题和简洁起见,省略了详细信息):
faViewModel = ViewModelProviders.of(getActivity()).get(FAViewModel.class);
faViewModel.getAllPosts().observe(getActivity(),
newPosts - > {
if (newPosts != null && newPosts.size() > 0) {
postsLoadingProgressBar.setVisibility(View.INVISIBLE);
}
// if ((posts == null) || newPosts.get(0).getId() != posts.get(0).getId()) {
// Update the cached copy of the posts in the adapter.
posts = newPosts;
mPostsAdapter = new PostsAdapter(this.getChildFragmentManager(), newPosts);
mViewPager.setAdapter(mPostsAdapter);
// }
});
faViewModel.fetchNextData(currentPage);
现在,您还可以在每个帖子上附加一个“喜欢”按钮,以及单个帖子已收到的喜欢总数。
现在,用户单击“喜欢”按钮,然后您调度一个动作。此操作可以执行以下操作:
1.1发出更新查询,以增加帖子获得的点赞次数。
1.2标记数据库中的此特定帖子已被用户喜欢。
第2步是可行的,所以我们不要谈论它。但是,步骤1.1和1.2有点棘手,因为当我发出更新查询时:
@Query("UPDATE post SET liked= :liked, likes = likes + 1 WHERE id= :id")
void updateLike(int id, int liked);
(注意:这不关心不喜欢。它的作用就像中等。用户可以给一个帖子提供N个喜欢。)
无论如何,当我发出该查询时,它将更新数据库。因为实际上我是使用LiveData
观察该数据库,所以我收到一个新的LiveData实体,然后将其附加到适配器上。我的适配器认为数据集已更改(并且认为正确),因此它更新了该列表。这样做时,它还会滚动到列表中的第一篇文章。 这是问题。
如何防止这种情况?
一种可能的解决方案是检查传入列表和当前列表的帖子ID是否相同,而不更新。但是,这意味着我不会从数据库中获取更新的No of likes
和其他内容。反过来,这意味着当用户单击“赞”按钮时,我将不得不手动更改No of likes
和“喜欢”可绘制对象的状态在视图中。
这是正确的方法吗?还是可以做一些事情,使数据库仍然是“ 单个事实来源”,同时仍然拥有“赞按钮”功能。
感谢其他想法/黑客。
答案 0 :(得分:1)
如果您使用的是RecyclerView.Adapter
,则可以使用DiffUtil
解决问题,这意味着您拥有RecyclerView
或ViewPager2
。
首先,您不应该在每次db发出新数据时都重新创建适配器。
有新数据可用时,可以使用DiffUtil.calculateDiff(DiffUtil.Callback cb)
获取DiffUtil.DiffResult
对象,然后在结果上调用dispatchUpdatesTo(Adapter adapter)
。
这样,新旧数据之间的所有更改将被单独应用。例如,假设您的旧列表为A,B,C
,如果新列表为A,X,B,C
,则仅X
将被添加到RecyclerView中(默认情况下甚至是动画)。
这是一个示例实现:
class ListDiffCalculator : DiffUtil.Callback() {
private var oldList = emptyList<MyModel>()
private var newList = emptyList<MyModel>()
fun computeDiff(oldList: List<MyModel>, newList: List<MyModel>): DiffUtil.DiffResult {
this.oldList = oldList
this.newList = newList
return DiffUtil.calculateDiff(this)
}
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) =
oldList[oldItemPosition].id == newList[newItemPosition].id
override fun getOldListSize() = oldList.size
override fun getNewListSize() = newList.size
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) =
oldList[oldItemPosition].hashCode() == newList[newItemPosition].hashCode()
}
您的适配器可以实现Observer<List<MyModel>>
,以便您可以使其直接观察数据库,为简单起见,这里省略了其他适配器内容:
class Adapter : RecyclerView.Adapter<ViewHolder>(), Observer<List<MyModel>> {
private var data = emptyList<MyData>()
override fun onChanged(newData: List<MyModel>?) {
newData?.let {
val diff = listDiffCalculator.computeDiff(data, newData)
this.data = newData
diff.dispatchUpdatesTo(this)
}
}
}
并观察:
aViewModel.getAllPosts().observe(getActivity(), adapter)