web-dev-qa-db-fra.com

Ajouter une vue dynamiquement à l'élément de RecyclerView

Je veux ajouter la variable supplémentaire TextView à l'élément du RecyclerView.Adapter en fonction de l'objet ModalObject envoyé au RecyclerView.Adapter

Exemple :

J'ai eu un objet modal avec la variable isAddText. Dans onCreateViewHolder va créer l'objet porteur avec l'envoi de itemView en tant que paramètre.

@Override
public SolventViewHolders onCreateViewHolder(ViewGroup parent, int viewType) {
    View layoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.solvent_list, null);
    SolventViewHolders rcv = new SolventViewHolders(layoutView);
    return rcv;
}

SolventViewHolders.Java

public SolventViewHolders(View itemView) {
    super(itemView);
    itemView.setOnClickListener(this);
    textView = (TextView) itemView.findViewById(R.id.country_name);
    imageView = (ImageView) itemView.findViewById(R.id.country_photo);
    //  Now i need add a view to the itemView parent based on ModalObject isAddView property and how to do that. 
}
17
user3607798

Le problème

Normalement, vous auriez besoin d'exécuter itemView.addView (view, layoutParams) pour ajouter une autre vue par programme à votre mise en page, mais avec ViewHolders, ils seront réutilisés par RecyclerView pour lier différents éléments de l'adaptateur. ou ce nouveau TextView que vous souhaitez ajouter apparaîtra lorsqu'un autre élément est à nouveau lié à cette instance de viewHolder.

Je peux penser à plusieurs façons d’atteindre votre objectif:


1. Changer la visibilité de la vue

La première consiste à ajouter cette textView à votre mise en page et à lui donner sa visibilité par défaut, et à la définir sur VISIBLE lorsque vous liez cet élément pour lequel vous avez besoin de cette TextView, telles que:

@Override
public void onBindViewHolder(AbstractViewHolder holder, int position) {
    holder.extraTextView.setVisibility(View.GONE);
    if(shouldShowExtraTextView) {
        holder.extraTextView.setVisibility(View.VISIBLE);
        holder.extraTextView.setText(/* your text */);
    }
    // proceed to bind item to holder
}

2. Implémenter différents ViewHolders

La deuxième façon est plus élégante, il s'agit de créer un type de ViewHolder différent. Tout d'abord, vous devez créer des constantes int pour les deux types de vues à afficher par RecyclerView. Placez dans votre adaptateur le code suivant:

private static final int VIEW_ORDINARY = 0;
private static final int VIEW_WITH_EXTRA_TEXT_VIEW = 1;

Puis implémentez la méthode getItemViewType (int position) de l'adaptateur:

@Override
public int getItemViewType(int position) {
    if(position == /* position of item that needs extra text view */) {
        return VIEW_WITH_EXTRA_TEXT_VIEW;
    } else {
        return VIEW_ORDINARY;
    }
}

La prochaine étape consiste à créer un titulaire différent en fonction du type renvoyé:

@Override
public SolventViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    if(viewType == VIEW_WITH_EXTRA_TEXT_VIEW) {
        View itemView = LayoutInflater.from(parent.getContext())
                                      .inflate(R.layout.solvent_list_with_extra_text_view, parent, false);
        return new SolventViewHolderWithExtraTextView(itemView);
    } else {
        View itemView = LayoutInflater.from(parent.getContext())
                                      .inflate(R.layout.solvent_list, parent, false);
        return new SolventViewHolder(itemView);
    }
}

Bien sûr, vous devez définir SolventViewHolderWithExtraTextView:

class SolventViewHolderWithExtraTextView extends SolventViewHolder {
    TextView extraTextView;

    public SolventViewHolderWithExtraTextView(View itemView) {
        super(itemView);
        this.extraTextView = (TextView) findViewById(R.id.extra_text_view);
    }
}

Et ensuite dans onBindViewHolder ():

@Override
public void onBindViewHolder(SolventViewHolder holder, int position) {
    // you can use inheritance to handle binding logic or check the item view type again:
    // bind ordinary view
    if(getItemViewType(position)) {
        // bind the extra textView
        ((SolventViewHolderWithExtraTextView)holder).extraTextView.setText("I hope this works");
    }
}

De cette façon, vous avez un autre type de ViewHolder pour vos éléments nécessitant un autre TextView, et il sera réutilisé dans cette catégorie. C'est un moyen privilégié de faire ce que vous voulez.


3. Ajouter/supprimer la vue dynamiquement

Une autre solution (qui est vraiment laide solution si vous me demandez) est d’ajouter et de supprimer dynamiquement une vue, telle que:

@Override
public void onBindViewHolder(AbstractViewHolder holder, int position) {
    if(shouldDisplayExtraTextView) {
        if(!holder.displaysExtraTextView() {
            holder.addExtraTextView();
        }
        holder.extraTextView.setText("This solution is really ugly");
        } else {
        if(holder.displaysExtraTextView() {
            holder.removeExtraTextView();
        }
    }
}

class SolventViewHolders extends RecyclerView.ViewHolder {
    TextView extraTextView;
    boolean viewAdded = false;

    public SolventViewHolders(View itemView) {
        super(itemView);
        itemView.setOnClickListener(this);
        textView = (TextView) itemView.findViewById(R.id.country_name);
        imageView = (ImageView) itemView.findViewById(R.id.country_photo);
         //  Now i need add a view to the itemView parent based on ModalObject isAddView property and how to do that. 

        extraTextView = new TextView();
}

public void addExtraTextView() {
    ((ViewGroup)itemView).addView(extraTextView, layoutParams);
    viewAdded = true;
}

public void removeExtraTextView() {
    ((ViewGroup)itemView).removeView(extraTextView);
    viewAdded = false;
}

public boolean displaysExtraTextView() {
    return viewAdded;
}

C'est vraiment moche, car pour ajouter view à itemView, vous devez le convertir en ViewGroup (ce qui n'est pas forcément le cas), assurez-vous qu'il s'agira toujours d'un ViewGroup ou de sa sous-classe. En outre, vous devez fournir des layoutParams appropriés pour ajouter extraTextView à itemView.

22
maciekjanusz

Vous pouvez également définir itemView.setHasTransientState(true), auquel cas la vue ne sera pas recyclée tant que setHasTransientState n'aura pas la valeur false.

2
grgrssll

Avec l'aide de la réponse de @ grgrssll, j'ai pu résoudre mon problème. Dans mon cas, j'avais besoin de N-nombre de TextViews à l'intérieur de chaque ligne.

Dans mon onBindViewHolder

@Override
public void onBindViewHolder(@NonNull final HistoryViewHolder vh, int position) {
    ...
    //Generating N-number of TextViews depending on size of itemsMap
    for (Map.Entry<String, Model> entry : itemsMap.entrySet()) {
        SelectedModel s = entry.getValue();
        // Set setHasTransientState to true
        vh.itemDetailsContainer.setHasTransientState(true);    
        // Dynamically adding the views
        vh.itemDetailsContainer.addView(getItemTextView(context, s.getItemName()));
        ...
    }
    //Set setHasTranssientState to false after loop
    vh.itemDetailsContainer.setHasTransientState(false);
    ...
}
1
ymerdrengene