android - 更新 Room 实体并观察列表

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

想象一下在主 Activity 中呈现给您的帖子列表(如 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);

现在,您还可以在每个帖子上附加一个赞按钮,以及单个帖子收到的赞总数。

现在,用户点击“赞”按钮,然后您分派(dispatch)一个操作。此操作可以执行以下操作:

1.1 发出更新查询,增加点赞数一个帖子已获得。

1.2 在数据库中标记此特定帖子已被用户点赞。

  1. 实际向服务器发送一个POST 请求 并在那里更新 Artifact 。 (除其他外,该用户喜欢的总数和该用户喜欢的帖子。)

步骤 2 是可行的,我们不谈它。但是,步骤 1.1 和 1.2 很棘手,因为当我发出更新查询时:

    @Query("UPDATE post SET liked= :liked, likes = likes + 1 WHERE id= :id")
    void updateLike(int id, int liked);

(注意:这不关心不喜欢。它像 medium 一样工作。一个用户可以给一个帖子 N 个赞。)

无论如何,当我发出该查询时,它会更新数据库。因为,我实际上是在使用 LiveData观察这个数据库,所以我收到了一个新的 LiveData 实体,然后将其附加到我的适配器。我的适配器认为(并且认为是正确的)数据集已更改,因此它更新列表。执行此操作时,它还会滚动到列表中的第一个帖子。 这就是问题所在

我怎样才能防止这种情况发生?

一种可能的解决方案是检查传入列表和当前列表的帖子 ID 是否相同,不要更新。但是,这意味着我不会从数据库中获得更新的 No of likes 和其他内容。这反过来意味着当用户点击赞按钮时,我必须手动更改 No of likes 和 Like drawable 的状态IN THE VIEW

这是正确的做法吗?或者我可以做些什么来让我的数据库仍然是“单一事实来源”,同时仍然拥有“赞”按钮功能。

感谢任何其他想法/技巧

最佳答案

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

首先,您不应该在每次数据库发出新数据时都重新创建适配器。

当有新数据可用时,您可以使用 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)

关于android - 更新 Room 实体并观察列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55632229/

相关文章:

java - ViewPager nullPointerException 错误

java - 在 Android 中保存 Drag 'n' Drop in RecyclerView 后所做的更改 - 我已阅读所有内容

安卓房间数据库。如何将我的模型类添加到 Room 数据库中

java - 分页库最初返回空列表

android - 在 Android 的 onClickListener 中多次观察到 LiveData

android - Sqlite 数据库应该只创建一次

Android:如何使用日语 Unihan 字形?

java - 在 kotlin 中使用接口(interface)时获取平台声明冲突

java - 使用 Volley 预填充房间数据库

database - 数据未插入数据库室 Android