web-dev-qa-db-fra.com

Utiliser une vue personnalisée dans un adaptateur RecyclerView?

J'ai une vue personnalisée de base qui ressemble à ceci:

public class CustomView extends RelativeLayout {

    private User user;

    private ImageView profilePicture;

    public CustomView(Context context) {
        super(context);
        init();
    }

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        inflate(getContext(), R.layout.custom_layout, this);

        profilePicture = (ImageView) findViewById(R.id.profilePicture);

        // ACCESS USER MODEL HERE
        // e.g. user.getUsername()
    }

}

Comme vous pouvez le voir, j'aimerais accéder aux données utilisateur dans la vue (c'est-à-dire: user.getUsername()).

J'ai également besoin de pouvoir utiliser la vue personnalisée dans un adaptateur RecyclerView.

Voici à quoi ressemble actuellement mon adaptateur:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {

    private Context context;

    private List<User> userData;

    public MyAdapter(Context context, List<User> userData) {
        this.context = context;
        this.userData = userData;
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        public ViewHolder(View v) {
            super(v);
        }
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(context);

        // HOW TO INFLATE THE CUSTOM VIEW?

        // ViewHolder viewHolder = new ViewHolder(customView);

        return viewHolder;
    }

    @Override
    public void onBindViewHolder(final ViewHolder holder, int position) {
        // ANYTHING HERE?
    }

    @Override
    public int getItemCount() {
        return userData.size();
    }

}

Comment puis-je gonfler la vue personnalisée dans l'adaptateur?
De plus, dois-je mettre quelque chose dans onBindViewHolder()?

Remarque: I must utilise une vue personnalisée, car j'utilise cette vue sous différents adaptateurs (c'est-à-dire pas seulement cet RecyclerView adaptateur).

13
user7638732

En supposant une classe CustomView qui ressemble à ceci:

public class CustomView extends RelativeLayout {
    private User user;
    private ImageView profilePicture;

    // override all constructors to ensure custom logic runs in all cases
    public CustomView(Context context) {
        this(context, null);
    }
    public CustomView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }
    public CustomView(
            Context context,
            AttributeSet attrs,
            int defStyleAttr,
            int defStyleRes
    ) {
        super(context, attrs, defStyleAttr, defStyleRes);

        // put all custom logic in this constructor, which always runs
        inflate(getContext(), R.layout.custom_layout, this);
        profilePicture = (ImageView) findViewById(R.id.profilePicture);
    }

    public void setUser(User newUser) {
        user = newUser;
        // ACCESS USER MODEL HERE
        // e.g. user.getUsername()
    }
}

Votre RecyclerView.Adapter et RecyclerView.ViewHolder pourrait ressembler à ceci:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
    // no Context reference needed—can get it from a ViewGroup parameter
    private List<User> userData;

    public MyAdapter(List<User> userData) {
        // make own copy of the list so it can't be edited externally
        this.userData = new ArrayList<User>(userData);
    }

    @Override
    public int getItemCount() {
        return userData.size();
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        // no need for a LayoutInflater instance—
        // the custom view inflates itself
        CustomView itemView = new CustomView(parent.getContext());
        // manually set the CustomView's size
        itemView.setLayoutParams(new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT
        ));
        return new ViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(final ViewHolder holder, int position) {
        holder.getCustomView().setUser(userData.get(position));
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        private CustomView customView;

        public ViewHolder(View v) {
            super(v);
            customView = (CustomView) v;
        }

        public CustomView getCustomView() {
            return customView;
        }
    }
}
  • CustomView gère sa propre configuration, qui se produit dans son propre constructeur et dans ce cas utilise l'inflation d'un fichier XML. (Alternativement, il pourrait configurer ses vues enfant par programmation.)
  • Pour cette raison, le RecyclerView.Adapter n'a pas besoin d'effectuer d'inflation - il crée simplement une nouvelle instance de CustomView et laisse le CustomView s'inquiéter de sa propre configuration.
  • La CustomView ne peut pas obtenir une instance User tant que sa méthode setUser n'est pas appelée, donc l'accès utilisateur ne peut pas se produire dans le constructeur. Dans tous les cas, sur une durée de vie de CustomView, un RecyclerView peut lui demander d'afficher des informations pour de nombreux utilisateurs différents à des moments différents. CustomView doit être en mesure de le faire. Par conséquent, une méthode setUser est introduite.
  • Étant donné que CustomView est instancié par du code plutôt que par XML, les attributs de taille ne peuvent pas être définis en XML. Par conséquent, le dimensionnement est effectué par programme après l'instanciation.
  • onBindViewHolder appelle simplement setUser sur le CustomView pour lier CustomView avec l'instance User correcte.
  • La classe ViewHolder n'est plus qu'un lien entre un élément RecyclerView et un CustomView.

Utilisation de vues personnalisées prédéfinies à partir d'une autre classe dans RecyclerViews (c'est-à-dire sans gonfler XML dans le RecyclerView.Adapter) ne semble jamais être discuté. Je pense que c'est une excellente idée même lorsque la vue personnalisée est exclusivement utilisée dans un RecyclerView, car elle favorise la séparation des préoccupations et respect du principe de responsabilité unique .

23
Alex Peters
CustomView extends RelativeLayout {

Vous avez déjà une vue (enfin, un ViewGroup)

COMMENT GONFLER LA VUE PERSONNALISÉE?

Vous n'avez pas besoin de ... Le but d'un objet Custom View est de ne pas avoir besoin de XML, donc pas d'inflation.

Vous pouvez créer new CustomView(), mais vous devez définir tous les paramètres de mise en page, ce qui semble plus propre en XML, je pense.


La plupart des didacticiels RecyclerView affichent cependant gonflage via XML .

View customView = inflater.inflate(...);
ViewHolder viewHolder = new ViewHolder(customView);

Cela devrait fonctionner car dans la chaîne de classe, vous avez CustomView > RelativeLayout > ViewGroup > View

LayoutInflater inflater = LayoutInflater.from(context);

Comme je l'ai déjà dit, vous n'en avez pas besoin s'il n'y a pas de fichier XML que vous souhaitez gonfler.

Vous n'avez pas non plus besoin de la variable context.

parent.getContext() est une bonne solution.

// ANYTHING HERE?

Eh bien, oui, vous devez "lier" le ViewHolder avec les données que le ViewHolder doit contenir.

Encore une fois, la plupart, sinon la totalité, des didacticiels couvrent cela.

4
cricket_007