【问题标题】:Android Architecture Components: using ViewModel for RecyclerView itemsAndroid 架构组件:将 ViewModel 用于 RecyclerView 项目
【发布时间】:2019-10-14 03:38:38
【问题描述】:

我正在试验架构组件,我想为 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 关联?这有可能吗?

编辑:玩它,我尝试通过以下方式放入 ViewModel:

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

它有效,但这是正确的方法吗?

【问题讨论】:

  • 你好 Jacopo,你可以用不同的方式使用适配器..
  • 你能给我一个例子或链接吗?
  • 您可以从下面的链接stackoverflow.com/questions/47432835/…查看答案
  • 如果你觉得有问题再问我...
  • ViewModelProviders.of((FragmentActivity) holder.getContext()).get(GalleryItemViewModel.class) 这不会在后续调用中返回相同的GalleryItemViewModel 对象吗?我猜你应该使用ViewModelProvider 的重载方法get(@NonNull String key, @NonNull Class&lt;T&gt; modelClass) 并通过唯一键来获得每个onBindViewHolder 调用的唯一视图模型。

标签: android mvvm android-recyclerview android-architecture-components


【解决方案1】:

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

这是一个带有 MVVM 代码示例的 link,可以帮助您。

【讨论】:

    【解决方案2】:

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

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

    此外,ViewModel 类不应或包含来自 Android 环境的最少类,并且不应保留对视图类的任何引用,因为它可能比视图寿命更长。

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

    从 Activity/Fragment 获得相同的 ViewModel
    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));
        }
    
    }
    

    这是一个数据类,而不是 architecture components page 声明的 ViewModel 类,它还导入不利于单元测试的视图类。

    是数据绑定+RecyclerView教程,这个类的正确命名不应该是..ViewModel。 Check out this tutorial 用于数据类并将其与 RecyclerView 绑定。

    【讨论】:

    • 会看看!谢谢你现在
    • ViewModel 类不应该有上下文引用。
    • 我写的不应该,请仔细阅读。 此外,ViewModel 类不应或包含来自 Android 环境的最少类,并且不应保留对视图类的任何引用,因为它可能比视图寿命更长。 该 sn-p 显示之前如何使用 ViewModel 并且不应现在使用,它是数据类,不是当前用于 ViewModel 的架构类。
    【解决方案3】:

    确保在获取 ViewModel 时分配一个唯一标识符,因为在后台 ViewModelProviders 会为您提供相同的实例,否则

    get(some unique id, GalleryItemViewModel.class);

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

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

    【讨论】:

    • 传入唯一的key 很好,但是在这种情况下不需要它,因为他在每个绑定上调用vm::setPost,这可能会重新分配所有字段。虽然我不确定他的做法是否会导致任何性能问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-05-12
    • 1970-01-01
    • 1970-01-01
    • 2021-01-06
    • 1970-01-01
    • 1970-01-01
    • 2018-01-12
    相关资源
    最近更新 更多