更新房间实体并观察列表

时间:2019-04-11 12:12:49

标签: android android-room android-architecture-components android-livedata

想象一下在主要活动中向您介绍的帖子列表(例如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标记数据库中的此特定帖子已被用户喜欢。

  1. 实际上是向服务器发送 POST请求,并在那里更新工件。 (除其他事项外,该用户的总点赞和帖子。)

第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和“喜欢”可绘制对象的状态在视图中

这是正确的方法吗?还是可以做一些事情,使数据库仍然是“ 单个事实来源”,同时仍然拥有“赞按钮”功能。

感谢其他想法/黑客

1 个答案:

答案 0 :(得分:1)

如果您使用的是RecyclerView.Adapter,则可以使用DiffUtil解决问题,这意味着您拥有RecyclerViewViewPager2

首先,您不应该在每次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)