Обновление прав комнаты и соблюдение списка - PullRequest
3 голосов
/ 11 апреля 2019

Представьте себе список постов (например, 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 Отметьте, что этот конкретный пост в базе данных понравился пользователю.

  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, которую затем присоединяю к адаптеру. Мой адаптер думает (и правильно думает), что набор данных изменил , и, следовательно, он обновляет список. При этом он также прокручивает до первого поста в списке. ЭТО ПРОБЛЕМА .

Как я могу предотвратить это?

Одним из возможных решений является проверка того, совпадают ли идентификаторы записей входящего и текущего списка, не обновляйте. Однако это означает, что я не буду получать обновленные No of likes и другие вещи из БД. Это, в свою очередь, означало бы, что когда пользователь нажимает кнопку «Мне нравится», мне нужно вручную изменить No of likes и состояние рисованного объекта «Мне нравится» В ВИДЕ .

Это правильный подход? Или есть что-то, что я могу сделать, чтобы моя база данных оставалась « единственным источником истины », при этом сохраняя возможность кнопки «Мне нравится».

Любые другие идеи / хаки приветствуются.

1 Ответ

1 голос
/ 11 апреля 2019

Вы можете использовать DiffUtil для решения вашей проблемы, если вы используете RecyclerView.Adapter, что означает, что у вас есть либо RecyclerView, либо ViewPager2.

Во-первых, вы не должны пересоздавать адаптер каждый раз, когда БД генерирует новые данные.

Когда доступны новые данные, вы можете использовать DiffUtil.calculateDiff(DiffUtil.Callback cb), чтобы получить объект DiffUtil.DiffResult, а затем вызвать dispatchUpdatesTo(Adapter adapter) для результата.

Таким образом, все изменения между старыми и новыми данными будут применяться индивидуально. Например, скажем, ваш старый список - A,B,C, если новый список - A,X,B,C, тогда в RecyclerView будет добавлено только X (по умолчанию даже с анимацией).

Вот пример реализации:

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)
...