Обновить 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