想象一下在主要活动中呈现给您的帖子列表(如Facebook)。潜在的“单一事实来源”是数据库和Android的房间用于获取和观察这个帖子列表。
这就是我观察数据的方式(由于许可证问题和简洁而省略了详细信息):
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实体,然后我将它附加到我的适配器上。我的适配器认为(并且正确地认为)数据集已经改变,因此它更新了列表。执行此操作时,它还会滚动到列表中的第一个帖子。这就是问题。
我怎么能防止这种情况?
一种可能的解决方案是检查传入列表的post id和当前列表是否相同,不要更新。但是,这意味着我不会从数据库中获取更新的No of likes
和其他内容。这反过来意味着当用户点击类似按钮时,我将手动改变No of likes
和Like drawable的状态在视图中。
这是正确的方法吗?或者有什么我可以做的仍然有我的数据库作为“单一的事实来源”,同时仍然有Like按钮工具。
任何其他想法/黑客都表示赞赏。
你可以使用DiffUtil
解决你的问题,如果你使用RecyclerView.Adapter
,这意味着你有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)