代码之家  ›  专栏  ›  技术社区  ›  jack_the_beast

Android架构组件:对RecyclerView项使用ViewModel

  •  36
  • jack_the_beast  · 技术社区  · 7 年前

    我正在试验架构组件,我想为RecyclerView的每个项目构建一个ViewModel。我不确定这在形式上是否正确,或者我应该坚持“老办法”。

    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();
        }
    }
    

    这是可行的,但这是正确的方法吗?

    3 回复  |  直到 5 年前
        1
  •  10
  •   Artem Mostyaev    7 年前

    有趣,但答案是-这是正确的方式,应该被接受:) 您可以进行一些代码清理和删除 GalleryItemViewModel 从…起 PostViewHolder ,因为您正在创建硬引用,而不是使用它。 然后直接进入 onBindViewHolder() 像这样使用它 holder.binding.setViewModel(vm);

    这是一个 link 用MVVM代码示例可以帮助你。

        2
  •  6
  •   Thracian    6 年前

    首先,ViewModel的正确实现应该通过扩展 android.arch.lifecycle.ViewModel . 扩展的示例 BaseObservable 这使得ViewModel类成为数据类,但它应该是表示类,因为它正在取代MVP模式的表示器。

    另一件事是 ViewModelProviders.of(context).get(Class.class) 为每个调用返回相同的ViewModel,它允许您在视图之间共享相同的数据。

    此外,ViewModel类不应该,或者包含来自Android环境的最小类,并且不应该保留对视图类的任何引用,因为它可能会超过视图。

    在第二个示例中,您可能使用

    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类 architecture components page 它还导入视图类,这不利于单元测试。

    这是数据绑定+回收视图教程,正确的命名不应该。。此类的ViewModel。 Check out this tutorial 并将其与RecyclerView绑定。

        3
  •  4
  •   Dan Riza    5 年前

    确保在获取ViewModel时指定了唯一标识符,因为在后台,ViewModelProviders会为您提供相同的实例

    获取( 某些唯一id ,GalleryItemViewModel。等级);

    因此,请尝试在那里添加一个id,如下所示:

     GalleryItemViewModel vm = ViewModelProviders.of((FragmentActivity) holder.getContext()).get(**some unique id**, GalleryItemViewModel.class);
        vm.setPost(list.get(position));
        holder.setViewModel(vm);