Обновить RecyclerView с помощью Android LiveData

Есть много примеров, как добавить новый список к адаптеру при изменении LiveData.

Я пытаюсь обновить одну строку (например, количество комментариев к записи) в огромном списке. Было бы глупо сбросить весь список, чтобы изменить только одно поле.

Я могу добавить наблюдателя на BindViewHolder, но не могу понять, когда мне следует удалить наблюдателя

@Override
public void onBindViewHolder(ViewHolder vh, int position) {
    Post post = getPost(position);
    vh.itemView.setTag(post);
    post.getLiveName().observeForever(vh.nameObserver);
    ... 
}

5 ответов

Как сказал @Lyla, вы должны наблюдать весь список как LiveData во Fragment или Activity, когда при получении изменений вы должны установить весь список на адаптер с помощью DiffUtil.

Поддельный код:

PostViewModel {
    LiveData<List<Post>> posts;  // posts comes from DAO or Webservice
}

MyFragment extends LifecycleFragment {
    PostAdapter postAdapter;

    ...

    void onActivityCreated() {
        ...
        postViewModel.posts.observer(this, (postList) -> {
            postAdapter.setPosts(postList);
        }
    }       
}

PostAdapter {
    void setPosts(List<Post> postList) {
        DiffUtil.DiffResult result = DiffUtil.calculateDiff(new DiffUtil.Callback() {...}
        ...
    }
}

Использование DiffUtil может помочь с обновлением одной строки в огромном списке. Затем вы можете использовать LiveData для переноса списка комментариев вместо одного комментария или атрибута комментария.

Вот пример использования DiffUtil в адаптере RecyclerView и список кода наблюдения LiveData во фрагменте.

Использование Transformations.switchMap() поменять основной Post объект. Тогда нет необходимости удалять и повторно добавлять наблюдателей при повторном использовании ячейки.

@Override
public void onBindViewHolder(PostViewHolder vh, int position) {
    Post post = getPost(position);
    vh.bind(post);
}

Тогда в вашем классе ViewHolder

public class PostViewHolder extends RecyclerView.ViewHolder {
    private final MutableLiveData<Post> post = new MutableLiveData<>();

    public PostViewHolder(View itemView) {
        super(itemView);

        LiveData<String> name = Transformations.switchMap(post, new Function<Post, LiveData<String>>() {
            @Override
            public LiveData<String> apply(Post input) {
                return input.getLiveName();
            }
        });

        name.observeForever(new Observer<String>() {
            @Override
            public void onChanged(@Nullable String name) {
                // use name
            }
        });
    }

    public void bind(Post post) {
        post.setValue(post);
    }
}

Я думаю, вам следует использовать LiveAdapter для адаптера RecyclerView вместо создания дополнительного класса для адаптера.

Он также имеет реализацию DiffUtil, поэтому будет обновлен только один элемент. и без вызова notifyDatasetChange.

      // Kotlin sample
LiveAdapter(
            data = liveListOfItems,
            lifecycleOwner = this@MainActivity,
            variable = BR.item )
           .map<Header, ItemHeaderBinding>(R.layout.item_header) {
               onBind{

               }
               onClick{

               }
               areContentsTheSame { old: Header, new: Header ->
                   return@areContentsTheSame old.text == new.text
               }
               areItemSame { old: Header, new: Header ->
                   return@areContentsTheSame old.text == new.text
               }
           }
           .map<Point, ItemPointBinding>(R.layout.item_point) {
               onBind{

               }
               onClick{

               }
               areContentsTheSame { old: Point, new: Point ->
                   return@areContentsTheSame old.id == new.id
               }
               areItemSame { old: Header, new: Header ->
                   return@areContentsTheSame old.text == new.text
               }
           }
           .into(recyclerview)

Добавить context на ваш adapterClass строитель: AdpaterClass(context: Context)

затем умный бросил context к AppCompactActivity

livedata.observe(context as AppCompatActivity, Observer {it ->

        //perform action on it(livedata.value)
    })

при звонке в adapter отовсюду activity, fragment пройти context в adpater

Другие вопросы по тегам