Quel est l'avantage de ViewHolder?

lorsque vous développez un programme Android; et vous voulez avoir un ArrayAdapter vous pouvez simplement avoir une classe (la plupart du temps avec ViewHolder suffixe) ou gonfler directement votre convertView et trouver votre vue par id.

Quel est donc l'avantage d'utiliser le viseur?

L'exemple des deux ici :

        if(convertView==null)
        {
            convertView = ((Activity)_context).getLayoutInflater().inflate(R.layout.row_phrase, null);
        }
((TextView)convertView.findViewById(R.id.txtPhrase)).setText("Phrase 01");  

Ou:

static class ViewHolder {   
ImageView leftIcon;   
TextView upperLabel;  
TextView lowerLabel;  
}

et enfin dans le getView:

ViewHolder holder = null;
  if (view == null) {
   view = LayoutInflater.from(context).inflate(R.layout.row_layout,
   null, false);
   holder = new ViewHolder();
   holder.leftIcon = (ImageView) view.findViewById(R.id.leftIcon);
32
demandé sur ZhiXingZhe - WangYuQi 2014-02-01 21:39:15

4 réponses

comprendre comment fonctionne listview recycling

Comment fonctionne le mécanisme de recyclage de ListView

vous ne pouvez pas recycler une rangée qui est actuellement utilisée. Le lien ci-dessus explique comment fonctionne le mécanisme de recyclage listview

alors quel est l'avantage d'utiliser le viseur?

citant docs

votre code pourrait appeler findViewById() souvent pendant le défilement de ListView, ce qui peut ralentir la performance. Même lorsque l'Adaptateur renvoie une vue gonflée pour Recyclage, vous devez quand même rechercher les éléments et les mettre à jour. Une façon de contourner l'utilisation répétée de findViewById() est d'utiliser le dessin de" vue holder".

    public View getView(int position, View convertView, ViewGroup parent) { 
             ViewHolder holder; 

             if (convertView == null) { // if convertView is null
                 convertView = mInflater.inflate(R.layout.mylayout, 
                         parent, false);
                 holder = new ViewHolder(); 
                     // initialize views  
                convertView.setTag(holder);  // set tag on view
            } else { 
                holder = (ViewHolder) convertView.getTag();
                        // if not null get tag 
                        // no need to initialize
            } 

            //update views here  
            return convertView; 
    }

vous avez manqué la partie importante convertView.setTag(holder) et holder = (ViewHolder) ConvertView.getTag()

http://developer.android.com/training/improving-layouts/smooth-scrolling.html

28
répondu Raghunandan 2017-05-23 12:09:59

ViewHolder design pattern est utilisé pour accélérer le rendu de votre ListView - en fait pour le faire fonctionner en douceur, findViewById est assez cher (il fait DOM parsing) lorsqu'il est utilisé chaque fois qu'un élément de liste est rendu, il doit traverser votre hiérarchie de mise en page et aussi instancier des objets . Étant donné que les listes peuvent reformuler leurs articles assez fréquemment au cours du défilement, ces frais généraux peuvent être importants.

, vous pouvez trouver une bonne explication de la façon dont travaille :

http://www.youtube.com/watch?v=wDBM6wVEO70&feature=youtu.be&t=7m

à partir de la minute 10, vous avez expliqué le modèle de conception de titulaire de vue par les experts de google.

[modifier]

findViewById n'instancie pas de nouveaux objets, il ne traverse que la hiérarchie - voici la référence http://androidxref.com/5.1.1_r6/xref/frameworks/base/core/java/android/view/ViewGroup.java#3610

15
répondu marcinj 2015-08-17 09:55:34

pendant que vous flânez dans votre ListView, il n'y a qu'une poignée de vues affichées à un moment donné. Cela signifie que vous n'avez pas à instancier une vue pour chaque élément de votre adaptateur; lorsqu'une vue s'éteint, elle peut être réutilisée, ou recyclé .

voir recyclage et le motif des présentoirs ne sont pas les mêmes. Le motif des voyants est uniquement pour réduire le nombre d'appels view.findViewById(int) que vous faites. Le motif des visualiseurs seulement fonctionne lorsque vous profitez de vue recyclage.

dans getView(int position, View convertView, ViewGroup parent) , le paramètre convertView est soit null soit c'est une vue qui a été recyclée: il aura toujours les données d'un élément de liste différent lié à elle.

sans le motif du viseur, vous pouvez toujours profiter du recyclage de la vue (c'est-à-dire non instanciation aveugle des vues):

public View getView(int position, View convertView, ViewGroup parent) {
  View view = convertView;
  if (view == null) {
    view = // inflate new view
  }

  ImageView imageView = (ImageView) view.findViewById(R.id.listitem_image);
  TextView textView = (TextView) view.findViewById(R.id.listitem_text);
  TextView timestampView = (TextView) view.findViewById(R.id.listitem_timestamp);
  ProgressBar progressSpinnerView = (ProgressBar) view.findViewById(R.id.progress_spinner);

  // TODO: set correct data for this list item
  // imageView.setImageDrawable(...)
  // textView.setText(...)
  // timestampView.setText(...)
  // progressSpinnerView.setProgress(...)

  return view;
}

ci-Dessus est un exemple de vue recyclage - nous ne gonflons pas une nouvelle vue pour chaque rangée; nous gonflons seulement une vue si nous ne sommes pas donnés à réutiliser. éviter d'avoir à gonfler une vue est la partie qui aidera certainement avec la performance lors de la défilement à travers votre liste: profitez du recyclage de vue.

alors, à quoi sert le viseur? Nous faisons actuellement 4x findViewById(int) pour chaque article, indépendamment du fait que la rangée elle-même existait déjà. Comme findViewById(int) itère récursivement vers le bas d'un groupe de vue jusqu'à ce qu'il trouve un descendant avec L'ID donné, c'est un peu inutile pour nos vues recyclées - nous retrouvons des vues que nous avons déjà des références.

évitez cela en utilisant un objet ViewHolder pour conserver les références aux sous-vues après que vous les "trouvez":

private static class ViewHolder {
  final TextView text;
  final TextView timestamp;
  final ImageView icon;
  final ProgressBar progress;

  ViewHolder(TextView text, TextView timestamp, ImageView icon, ProgressBar progress) {
    this.text = text;
    this.timestamp = timestamp;
    this.icon = icon;
    this.progress = progress;
  }
}

View.setTag(Object) vous permet de dire à la Vue de tenir un objet arbitraire. Si nous l'utilisons pour tenir une instance de notre Afficheur après avoir fait nos appels findViewById(int) , nous pouvons utiliser View.getTag() sur les vues recyclées pour éviter d'avoir à faire les appels encore et encore.

public View getView(int position, View convertView, ViewGroup parent) {
  View view = convertView;
  if (view == null) {
    view = // inflate new view
    ViewHolder holder = createViewHolderFrom(view);
    view.setTag(holder);  
  }
  ViewHolder holder = view.getTag();
  // TODO: set correct data for this list item
  // holder.icon.setImageDrawable(...)
  // holder.text.setText(...)
  // holder.timestamp.setText(...)
  // holder.progress.setProgress(...)
  return view;
}

private ViewHolder createViewHolderFrom(View view) {
    ImageView icon = (ImageView) view.findViewById(R.id.listitem_image);
    TextView text = (TextView) view.findViewById(R.id.listitem_text);
    TextView timestamp = (TextView) view.findViewById(R.id.listitem_timestamp);
    ProgressBar progress = (ProgressBar) view.findViewById(R.id.progress_spinner);

    return new ViewHolder(text, timestamp, icon, progress);
}

les avantages de performance de cette optimisation sont discutables , mais c'est le avantage du titulaire de la vue .

9
répondu ataulm 2015-08-16 11:39:34

premièrement:

dans ListView lorsque vous faites défiler le ListView vous devez créer un nouvel article et lier ses données sur elle si vous avez beaucoup d'articles dans ListView il peut causer des fuites de mémoire parce que plus d'objets que vous avez créé pour les articles , mais Android en utilisant le concept de recycle la plupart de son API , et il signifie que vous créez un objet et l'utilisez au lieu de le détruire et de déclarer nouveau, donc, lorsque vous faites défiler ListView API en utilisant les éléments invisibles que vous scrolled et passez-le pour vous dans getView méthode il est convertView donc ici, vous dealez avec plus d'articles ListView

Deuxièmement:

si vous avez un article personnalisé dans ListView vous devez joindre votre disposition personnalisée sur chaque article du ListView de sorte que vous serez à chaque fois ListView lier nouvel article en utilisant findViewById pour obtenir la référence des éléments de la disposition. Cette méthode ira à la recherche de votre article de manière récursive donc vous ViewHolder vous aidera pour faire le récursif fait pour une fois seulement et puis il tiendra la référence de l'élément de mise en page pour vous jusqu'à ce que vous pouvez le joindre pour ListView

espère que cela vous aidera et me nourrir de retour dans tout pas chose évidente

2
répondu mohammed momn 2014-02-01 17:51:36