I'm experimenting with the Architecture Components, and I want to build a ViewModel for each item of a RecyclerView. I'm not sure if that is formally correct or I should stick with the "old way".
I have this adapter:
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(); } }
which works fine but it's very basic. how do I update it so each item has it's own ViewModel associated? is that even possible?
EDIT: playing with it, I've tried to put in ViewModels the following way:
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(); } }
it works but is that the correct way to do it?
A ViewHolder describes an item view and metadata about its place within the RecyclerView. Adapter implementations should subclass ViewHolder and add fields for caching potentially expensive findViewById results.
Simple answer: You should use RecyclerView in a situation where you want to show a lot of items, and the number of them is dynamic. ListView should only be used when the number of items is always the same and is limited to the screen size.
bindViewHolder. This method internally calls onBindViewHolder to update the ViewHolder contents with the item at the given position and also sets up some private fields to be used by RecyclerView.
Funny, but answer - This is correct way, should be accepted :) You can make some code clean-up and remove GalleryItemViewModel
from PostViewHolder
, because you are creating hard reference and not using it. Then dirrectly in onBindViewHolder()
use it like holder.binding.setViewModel(vm);
This is a link with MVVM code example that can help you.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With