web-dev-qa-db-fra.com

Comment fonctionne le mécanisme de recyclage de ListView

Donc, j'ai ce problème que j'avais avant, et naturellement j'ai demandé de l'aide sur ici . La réponse de Luksprog a été excellente car je ne savais pas comment ListView et GridView s'optimisaient avec le recyclage des vues. Donc, grâce à ses conseils, j'ai pu changer la façon dont j'ai ajouté Views à GridView. Le problème est maintenant que j'ai quelque chose qui n'a pas de sens. Ceci est mon getView de mon BaseAdapter:


public View getView(int position, View convertView, ViewGroup parent) {
        if(convertView == null) {
            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
            convertView = inflater.inflate(R.layout.day_view_item, parent, false);
        }
        Log.d("DayViewActivity", "Position is: "+position);
        ((TextView)convertView.findViewById(R.id.day_hour_side)).setText(array[position]);
        LinearLayout layout = (LinearLayout)convertView.findViewById(R.id.day_event_layout);

        //layout.addView(new EventFrame(parent.getContext()));

        TextView create = new TextView(DayViewActivity.this);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 62, getResources().getDisplayMetrics()), 1.0f);
        params.topMargin = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics());
        params.bottomMargin = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics());
        create.setLayoutParams(params);
        create.setBackgroundColor(Color.BLUE);
        create.setText("Test"); 
        //the following is my original LinearLayout.LayoutParams for correctly setting the TextView Height
        //new LinearLayout.LayoutParams(0, (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 60, getResources().getDisplayMetrics()), 1.0f)   
        if(position == 0) {
            Log.d("DayViewActivity", "This should only be running when position is 0. The position is: "+position);
            layout.addView(create);
        }

        return convertView;
    }

}

Le problème est que lorsque je fais défiler, cela se produit, et non sur la position 0 ... Ressemble à la position 6 et à la position 8, plus il en met deux en position 8. Maintenant, j'essaie toujours de comprendre comment utiliser ListView et GridView. ne comprends pas pourquoi cela se produit. L'une des principales raisons pour lesquelles je pose cette question est d'aider les autres personnes qui ne connaissent probablement pas List View et la vue de recyclage de GridView, ou la façon dont cela article , mécanisme ScrapView.

enter image description here

Éditer plus tard

Ajouter un lien à une conversation google IO est tout ce dont vous avez besoin pour comprendre le fonctionnement de ListView. Le lien était mort dans les commentaires. Donc, user3427079 était assez gentil pour mettre à jour ce lien. Ici c'est pour un accès facile.

141
Andy

Au départ, je ne connaissais pas non plus le recyclage par liste de diffusion ni le mécanisme d’utilisation de convertview, mais après une journée de recherche, je comprends assez bien les mécanismes de la vue en liste en faisant référence à une image de Android.amberfog enter image description here

Chaque fois que votre liste est remplie par un adaptateur, elle indique en gros le nombre de lignes que la liste peut afficher à l'écran et le nombre de lignes n'augmente même pas lorsque vous faites défiler la liste. C’est le truc Android utilise pour que listview fonctionne plus rapidement et efficacement. Maintenant, le récit interne de listview faisant référence à l’image, comme vous pouvez le constater, a initialement 7 éléments visibles, puis, si vous faites défiler jusqu'à ce que l'élément 1 ne soit plus visible, getView () transmet cette vue (c'est-à-dire l'élément1) au recycleur et vous pouvez utiliser

System.out.println("getview:"+position+" "+convertView);

à l'intérieur de votre

public View getView(final int position, View convertView, ViewGroup parent)
{
    System.out.println("getview:"+position+" "+convertView);
    ViewHolder holder;
    View row=convertView;
    if(row==null)
    {
        LayoutInflater inflater=((Activity)context).getLayoutInflater();
        row=inflater.inflate(layoutResourceId, parent,false);

        holder=new PakistaniDrama();
        holder.tvDramaName=(TextView)row.findViewById(R.id.dramaName);
        holder.cbCheck=(CheckBox)row.findViewById(R.id.checkBox);

        row.setTag(holder);

    }
    else
    {
        holder=(PakistaniDrama)row.getTag();
    }
            holder.tvDramaName.setText(dramaList.get(position).getDramaName());
    holder.cbCheck.setChecked(checks.get(position));
            return row;
    }

Vous remarquerez que, dans votre logcat, convertview a initialement la valeur null pour toutes les lignes visibles, car il n’existait initialement aucune vue (c.-à-d. Éléments) dans le recycleur. Votre getView () crée donc une nouvelle vue pour chacun des éléments visibles. Au moment où vous faites défiler l'écran et que l'élément 1 sort de l'écran, il sera envoyé au recycleur avec son état actuel (par exemple, le 'texte' de TextView ou dans le cas de la mienne, si la case à cocher est cochée, elle sera associée à la vue et stockée dans le recycleur).

Désormais, lorsque vous faites défiler l'écran vers le haut/le bas, votre liste de vues ne créera pas de nouvelle vue, elle utilisera la vue qui se trouve dans votre recycleur. Dans votre Logcat , vous remarquerez que 'convertView' n'est pas null, c'est parce que votre nouvel élément 8 sera dessiné à l'aide de convertview, c'est-à-dire qu'il faut item 1 vue du recycleur et gonfle le point 8 à sa place, et vous pouvez le constater dans mon code. Si vous aviez une case à cocher et que vous la cochez à la position 0 (disons que item1 avait une case à cocher et que vous l'avez cochée), alors lorsque vous faites défiler l'écran, la case à cocher de l'élément 8 est déjà cochée, c'est pourquoi listview utilise la même vue, ne pas créer un nouveau pour vous en raison de l'optimisation des performances.

Choses importantes

1 . Ne définissez jamais les paramètres layout_height Et layout_width De votre liste de diffusion sur wrap_content, Car getView() forcera votre adaptateur à obtenir un enfant pour mesurer la hauteur des vues. être dessiné en mode liste et peut entraîner un comportement inattendu, tel que renvoyer convertview même si la liste ne défile pas. utilisez toujours match_parent ou largeur/hauteur fixe.

2 . Si vous souhaitez utiliser une mise en page ou une vue après l'affichage de votre liste et qu'une question vous est peut-être venue à l'esprit si je règle layout_height Sur fill_parent, La vue après l'affichage de la liste n'apparaîtra pas à la baisse. l’écran, il est donc préférable de placer votre liste dans une mise en page. Par exemple, Mise en page linéaire, définissez la hauteur et la largeur de cette mise en page selon vos besoins et définissez la hauteur et width attribut de votre listview à en tant que de votre mise en page (comme si votre largeur de mise en page est 320 et la hauteur est 280) , alors votre liste doit avoir la même hauteur et width . Ceci indiquera à getView () la hauteur et la largeur exactes des vues à rendre, et getView () n'appellera pas encore et encore des lignes aléatoires, et d'autres problèmes tels que le retour de la vue convertie avant même que le défilement ne se produise, j'ai testé Si moi-même, listview ne se trouvait pas à l'intérieur de lineaLayout, il rencontrait également des problèmes de répétition de l'appel de vue et de la conversion de vues, placer la vue de liste à l'intérieur de LinearLayout fonctionnait comme par magie. (Je ne savais pas pourquoi).

01-01 14:49:36.606: I/System.out(13871): getview 0 null
01-01 14:49:36.636: I/System.out(13871): getview 0 Android.widget.RelativeLayout@406082c0
01-01 14:49:36.636: I/System.out(13871): getview 1 Android.widget.RelativeLayout@406082c0
01-01 14:49:36.646: I/System.out(13871): getview 2 Android.widget.RelativeLayout@406082c0
01-01 14:49:36.646: I/System.out(13871): getview 3 Android.widget.RelativeLayout@406082c0
01-01 14:49:36.656: I/System.out(13871): getview 4 Android.widget.RelativeLayout@406082c0
01-01 14:49:36.666: I/System.out(13871): getview 5 Android.widget.RelativeLayout@406082c0
01-01 14:49:36.666: I/System.out(13871): getview 0 Android.widget.RelativeLayout@406082c0
01-01 14:49:36.696: I/System.out(13871): getview 0 Android.widget.RelativeLayout@406082c0
01-01 14:49:36.706: I/System.out(13871): getview 1 null
01-01 14:49:36.736: I/System.out(13871): getview 2 null
01-01 14:49:36.756: I/System.out(13871): getview 3 null
01-01 14:49:36.776: I/System.out(13871): getview 4 null

Mais maintenant, c'est résolu, je sais, je ne suis pas très doué pour expliquer, mais comme je l'ai mis toute ma journée à comprendre, j'ai donc pensé que d'autres débutants comme moi pourraient obtenir de l'aide de mon expérience et j'espère que vous aurez un peu plus de compréhension du cadre ListView comment cela fonctionne, car il est vraiment compliqué et compliqué, de sorte que les débutants ont trop de difficulté à le comprendre

318
Muhammad Babar

Prenez garde, dans le modèle Titulaire, si vous définissez la position dans votre objet Titulaire, vous devez la définir à chaque fois, par exemple:

@Override
public final View getView(int position, View view, ViewGroup parent) {
    Holder holder = null;
    if (view == null) {
        LayoutInflater inflater = (LayoutInflater) App.getContext()
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = inflater.inflate(getContainerView(), parent, false);
        holder = getHolder(position, view, parent);
        holder.setTag(tag);
        view.setTag(holder);
    } else {
        holder = (Holder) view.getTag();
    }
    holder.position = position;
    draw(holder);
    return holder.getView();
}

c'est un exemple d'une classe abstraite, où

getHolder(position, view, parent);

fait toutes les opérations de réglage pour le

ImageViews, TextViews, etc..
8
Ahmed Adel Ismail

En utilisant le modèle de titulaire, vous pouvez obtenir ce que vous voulez:

Vous pouvez trouver la description de ce modèle ici:

Le recyclage de la vue liste se produit lorsque vous faites défiler l'écran et que les éléments de la liste ci-dessus sont masqués. Ils sont réutilisés pour afficher les nouveaux éléments de vue liste.

2
Krishna

Recyclerview est juste une classe d'encapsulation permettant de gérer l'ajout de vues dynamiques de manière efficace, c'est-à-dire en réutilisant la vue déjà créée.

Il suffit de penser à recyclerview, l’ordre d’appel des méthodes est-

  • getItemCount - Ceci est appelé en premier pour que l'adaptateur sache combien de vues doivent être créées ou gérées.
  • getItemViewType- Ceci est appelé ensuite pour savoir quel type de vues doit être gonflé.
  • onCreateViewHolder - Ceci est appelé ensuite pour savoir quelle vue doit être gonflée.
  • onBindViewHolder: à ce stade, l’adaptateur vous donnera l’instance de viewholder qui contient la vue gonflée par rapport à la position. Vous pouvez donc définir les données sur les vues.
0
Shivam Yadav