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

Android Architecture Components: using ViewModel for RecyclerView items

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

我有这个适配器:

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

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

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

这是一个 link MVVM 代码示例,可以为您提供帮助。

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

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

此外,ViewModel class 不应或包含来自 Android 环境的最少 classes,并且不应保留任何对视图 classes 的引用,因为它可能会过时观点。

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

从 Activity/Fragment 获得相同的 ViewModel
public void setViewModel(GalleryItemViewModel viewModel){
            this.viewModel = viewModel;
            binding.setViewModel(viewModel);
}

能否分享布局文件以及如何使用 ViewModel 实现此文件class?

Link 已接受答案中的样本不是 MVVM 和数据绑定的正确示例。

来自 link 的 ViewModel class 设置为您的第二个示例:

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

}

这是一个数据 class,而不是 architecture components page 所述的 ViewModel class,它还导入了不利于单元测试的视图 classes。

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

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

get(一些唯一的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);