Компоненты архитектуры 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? это вообще возможно?

EDIT: играя с ним, я попытался поместить в 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();
    }
}

это работает, но это правильный способ сделать это?

2 ответов


смешно, но ответ-это правильный способ, должен быть принят :) Вы можете сделать некоторую очистку кода и удалить GalleryItemViewModel с PostViewHolder, потому что вы создаете жесткую ссылку, а не использовать его. Затем непосредственно в onBindViewHolder() использовать его как holder.binding.setViewModel(vm);

это ссылке С примером кода MVVM, который может вам помочь.


прежде всего правильная реализация 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.