Компоненты архитектуры Android: использование ViewModel для элементов RecyclerView
Я экспериментирую с компонентами архитектуры и хочу создать ViewModel для каждого элемента RecyclerView. Я не уверен, является ли это формально правильным, или я должен придерживаться "старого способа".
У меня есть этот адаптер:
public class PostAdapter extends RecyclerView.Adapter<PostAdapter.PostViewHolder> {
private List<Post> list;
public static class PostViewHolder extends RecyclerView.ViewHolder{
final ItemPostBinding binding;
public PostViewHolder(ItemPostBinding binding){
super(binding.getRoot());
this.binding = binding;
}
}
@Override
public PostViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
ItemPostBinding binding = DataBindingUtil
.inflate(LayoutInflater.from(parent.getContext()), R.layout.item_post,
parent, false);
return new PostViewHolder(binding, parent.getContext());
}
@Override
public void onBindViewHolder(PostViewHolder holder, int position) {
holder.binding.setPost(list.get(position));
holder.binding.executePendingBindings();
}
@Override
public int getItemCount() {
return list == null ? 0 : list.size();
}
public void setList(List<Post> list){
this.list = list;
notifyDataSetChanged();
}
}
который работает хорошо, но это очень просто. как мне обновить его, чтобы у каждого элемента была своя собственная ViewModel? это вообще возможно?
ОБНОВЛЕНИЕ: играя с ним, я попытался вставить в ViewModels следующим образом:
public class PostAdapter extends RecyclerView.Adapter<PostAdapter.PostViewHolder> {
private List<Post> list;
public static class PostViewHolder extends RecyclerView.ViewHolder{
final ItemPostBinding binding;
private final Context context;
private GalleryItemViewModel viewModel;
public PostViewHolder(ItemPostBinding binding, Context context){
super(binding.getRoot());
this.binding = binding;
this.context = context;
}
public Context getContext(){
return context;
}
public void setViewModel(GalleryItemViewModel viewModel){
this.viewModel = viewModel;
binding.setViewModel(viewModel);
}
}
@Override
public PostViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
ItemPostBinding binding = DataBindingUtil
.inflate(LayoutInflater.from(parent.getContext()), R.layout.item_post,
parent, false);
return new PostViewHolder(binding, parent.getContext());
}
@Override
public void onBindViewHolder(PostViewHolder holder, int position) {
GalleryItemViewModel vm = ViewModelProviders.of((FragmentActivity) holder.getContext()).get(GalleryItemViewModel.class);
vm.setPost(list.get(position));
holder.setViewModel(vm);
}
@Override
public int getItemCount() {
return list == null ? 0 : list.size();
}
public void setList(List<Post> list){
this.list = list;
notifyDataSetChanged();
}
}
это работает, но это правильный способ сделать это?
Ответы
Ответ 1
Забавно, но ответ - это правильный путь, должны быть приняты :) Вы можете сделать некоторые код очистки и удаления GalleryItemViewModel
из PostViewHolder
, потому что вы создаете жесткую ссылку, и она не используется. Затем непосредственно в onBindViewHolder()
используйте его как holder.binding.setViewModel(vm);
Это ссылка с примером кода MVVM, которая может вам помочь.
Ответ 2
Прежде всего, правильная реализация ViewModel должна быть расширена android.arch.lifecycle.ViewModel
. Примеры, которые расширяют BaseObservable
что делает класс ViewModel классом данных, но он должен быть классом представления, поскольку он заменяет ведущего шаблона MVP.
Другое дело - ViewModelProviders.of(context).get(Class.class)
возвращает один и тот же ViewModel для каждого вызова и позволяет вам делиться одними и теми же данными между представлениями.
Кроме того, класс ViewModel не должен содержать минимальные классы из среды Android и не должен содержать ссылки на классы, поскольку он может пережить взгляды.
В вашем втором примере вы, вероятно, получаете тот же ViewModel из Activity/Fragment, используя
public void setViewModel(GalleryItemViewModel viewModel){
this.viewModel = viewModel;
binding.setViewModel(viewModel);
}
Можете ли вы поделиться файлом макета и как его реализовать с помощью класса ViewModel?
Ссылка на образец в принятом ответе не является правильным примером для MVVM и привязки данных.
Класс ViewModel из набора ссылок в качестве второго примера:
public class CommentHeaderViewModel extends BaseObservable {
private Context context;
private Post post;
public CommentHeaderViewModel(Context context, Post post) {
this.context = context;
this.post = post;
}
public String getCommentText() {
return Html.fromHtml(post.text.trim()).toString();
}
public String getCommentAuthor() {
return context.getResources().getString(R.string.text_comment_author, post.by);
}
public String getCommentDate() {
return new PrettyTime().format(new Date(post.time * 1000));
}
}
Это класс данных, а не класс ViewModel как состояния страницы компонентов архитектуры, он также импортирует классы классов, которые плохо подходят для модульного тестирования.
Это привязка данных + учебник RecyclerView, правильное именование не должно быть.ViewModel для этого класса. Ознакомьтесь с этим руководством по классу данных и привяжите его к RecyclerView.
Ответ 3
Убедитесь, что вы присваиваете уникальный идентификатор при получении ViewModel, потому что под капотом ViewModelProviders даст вам тот же экземпляр в противном случае
get (некоторый уникальный идентификатор, GalleryItemViewModel.class);
Поэтому, пожалуйста, попробуйте добавить идентификатор, как показано ниже:
GalleryItemViewModel vm = ViewModelProviders.of((FragmentActivity) holder.getContext()).get(**some unique id**, GalleryItemViewModel.class);
vm.setPost(list.get(position));
holder.setViewModel(vm);