it-swarm.com.de

Android-Architekturkomponenten: Verwenden von ViewModel for RecyclerView-Elementen

Ich experimentiere mit den Architekturkomponenten und möchte für jedes Element einer RecyclerView ein ViewModel erstellen. Ich bin mir nicht sicher, ob das formal korrekt ist oder ich sollte beim "alten Weg" bleiben.

Ich habe diesen Adapter: 

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

was gut funktioniert, aber es ist sehr einfach. Wie aktualisiere ich es, damit jedem Element ein eigenes ViewModel zugeordnet ist? ist das überhaupt möglich?

BEARBEITEN: Ich spiele damit und habe versucht, ViewModels auf folgende Weise einzufügen:

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

es funktioniert, aber ist das der richtige Weg?

17
jack_the_beast

Witzig, aber Antwort - Dies ist der richtige Weg, sollte akzeptiert werden:) Sie können einige Code-Bereinigung vornehmen und GalleryItemViewModel von PostViewHolder entfernen, da Sie einen harten Verweis erstellen und ihn nicht verwenden . Dann direkt in onBindViewHolder() benutze es wie holder.binding.setViewModel(vm); 

Dies ist ein link mit MVVM-Codebeispiel, das Ihnen helfen kann.

3

Die korrekte Implementierung von ViewModel sollte zunächst durch die Erweiterung von Android.Arch.lifecycle.ViewModel erfolgen. Die Beispiele für die Erweiterung von BaseObservable, die die ViewModel-Klasse zu einer Datenklasse macht, sollten jedoch Präsentationsklasse sein, da sie den Moderator des MVP-Musters ersetzt.

Eine andere Sache ist, dass ViewModelProviders.of(context).get(Class.class) dasselbe ViewModel für jeden Anruf zurückgibt und es Ihnen ermöglicht, dieselben Daten zwischen Ansichten zu teilen.

Außerdem sollte die ViewModel-Klasse keine Mindestklassen aus der Android-Umgebung enthalten oder enthalten und sollte keine Verweise auf Ansichtsklassen enthalten, da die Ansichten möglicherweise überleben.

In Ihrem zweiten Beispiel erhalten Sie wahrscheinlich dasselbe ViewModel von Activity/Fragment 

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

Können Sie die Layoutdatei gemeinsam nutzen und wie Sie diese mit der ViewModel-Klasse implementieren?

Der Link zum Beispiel in akzeptierter Antwort ist kein korrektes Beispiel für MVVM und Datenbindung.

ViewModel-Klasse aus dem Link, der als zweites Beispiel festgelegt wurde:

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

}

Dies ist eine Datenklasse, keine ViewModel-Klasse als Architekturkomponenten-Seite state. Außerdem werden View-Klassen importiert, was für Unit-Tests ungeeignet ist.

Es ist Datenbindung + RecyclerView Tutorial, korrekte Benennung sollte nicht ..ViewModel für diese Klasse sein. Schauen Sie sich dieses Tutorial an für Datenklasse und binden Sie es mit RecyclerView.

2
Thracian

Vergewissern Sie sich, dass Sie beim Abrufen des ViewModel eine eindeutige Kennung vergeben, da die ViewModelProvider Ihnen ansonsten dieselbe Instanz zur Verfügung stellen.

get (eine eindeutige ID, GalleryItemViewModel.class);

Versuchen Sie also, dort eine ID wie folgt hinzuzufügen:

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