web-dev-qa-db-ja.com

Android Architecture Components:RecyclerViewアイテムにViewModelを使用する

アーキテクチャコンポーネントを実験していて、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();
    }
}

それは動作しますが、それを行う正しい方法ですか?

24
jack_the_beast

おかしいが、答え-これは正しい方法です、受け入れられるべきです:)ハード参照を作成して使用していないので、コードをクリーンアップしてGalleryItemViewModelからPostViewHolderを削除できます。次に、onBindViewHolder()で直接holder.binding.setViewModel(vm);のように使用します

これは link であり、MVVMコードの例が役立ちます。

8

まず、ViewModelを正しく実装するには、_Android.Arch.lifecycle.ViewModel_を拡張する必要があります。 ViewModelクラスをデータクラスにするBaseObservableを拡張する例ですが、MVPパターンのプレゼンターを置き換えるため、プレゼンテーションクラスにする必要があります。

もう1つは、ViewModelProviders.of(context).get(Class.class)がすべての呼び出しに対して同じViewModelを返し、ビュー間で同じデータを共有できるようにすることです。

また、ViewModelクラスは、Android環境からの最小クラスを含むべきではありません。また、ビューよりも存続する可能性があるため、ビュークラスへの参照を保持しないでください。

2番目の例では、おそらくアクティビティ/フラグメントから同じViewModelを取得します

_public void setViewModel(GalleryItemViewModel viewModel){
            this.viewModel = viewModel;
            binding.setViewModel(viewModel);
}
_

レイアウトファイルを共有し、ViewModelクラスでこれを実装する方法を教えてください。

受け入れられた回答のサンプルのリンクは、MVVMおよびデータバインディングの正しい例ではありません。

2番目の例として、リンクセットの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とのバインド。

3
Thracian

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);
1
Dan Riza