web-dev-qa-db-fra.com

ListView dans l'ordre ArrayAdapter est mélangé lors du défilement

J'ai un ListView dans un ArrayAdapter personnalisé qui affiche une icône ImageView et un TextView dans chaque ligne. Lorsque je fais la liste assez longtemps pour vous permettre de la parcourir, l'ordre commence correctement, mais lorsque je commence à faire défiler l'écran vers le bas, certaines des entrées précédentes commencent à réapparaître. Si je reviens en arrière, l'ordre ancien change. Si vous répétez cette opération, l'ordre de la liste entière semble finalement être aléatoire. Le défilement de la liste entraîne donc la modification de l'ordre des enfants ou le rafraîchissement du dessin n'est pas correct.

Qu'est-ce qui pourrait provoquer quelque chose comme ça? J'ai besoin que l'ordre dans lequel les éléments sont affichés à l'utilisateur soit identique à l'ordre dans lequel ils ont été ajoutés à ArrayList, ou au moins pour rester dans un ordre statique. Si j'ai besoin de fournir des informations plus détaillées, s'il vous plaît faites le moi savoir. Toute aide est appréciée. Merci.

38
Dan B

J'avais des problèmes similaires, mais lorsque vous cliquez sur un élément de la liste personnalisée, les éléments à l'écran s'inversent dans l'ordre. Si je cliquais à nouveau, ils reviendraient à leur position d'origine.

Après avoir lu ceci, j'ai vérifié mon code et surchargé la méthode getView. La vue convertie me donnait la vue, et si elle était nulle, je construirais mes fichiers. Cependant, après avoir placé un point d'arrêt, j'ai constaté qu'elle appelait cette méthode à chaque clic et que, lors des clics suivants, la vue convertie n'était pas nulle. Par conséquent, les éléments n'étaient pas définis.

Voici un exemple de ce que c'était:


public View getView(int position, View convertView, ViewGroup parent)
{
    View view = convertView;
    if (view == null)
    {
        LayoutInflater vi = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = vi.inflate(R.layout.listitemrow, null);

        RssItem rssItem = (RssItem) super.getItem(position);
        if (rssItem != null)
        {
            TextView title = (TextView) view.findViewById(R.id.rowtitle);
            if (title != null)
            {
                title.setText(rssItem.getTitle());
            }
        }
    }
    return view;
}

Le changement subtil déplace l’accolade proche pour le contrôle nul sur la vue juste après le gonflage:


public View getView(int position, View convertView, ViewGroup parent)
{
    View view = convertView;
    if (view == null)
    {
        LayoutInflater vi = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = vi.inflate(R.layout.listitemrow, null);
    }
    RssItem rssItem = (RssItem) super.getItem(position);
    if (rssItem != null)
    {
        TextView title = (TextView) view.findViewById(R.id.rowtitle);
        if (title != null)
        {
            title.setText(rssItem.getTitle());
        }
    }
    return view;
}

J'espère que cela aide les autres qui vivent ce même problème.

56
farcrats

Pour clarifier davantage la réponse de farcats ci-dessous de manière plus générale, voici mon explication:

L'opération vi.inflate (nécessaire ici pour analyser la mise en forme d'une ligne à partir de XML et créer l'objet View approprié) est encapsulée par un if (vue == null) instruction pour efficacité, de sorte que l'inflation du même objet ne se reproduira pas à chaque fois qu'il apparaît.

CEPENDANT, les autres parties de la méthode getView sont utilisées pour définir d'autres paramètres et ne doivent donc PAS être incluses dans l'instruction if (view == null) .

De même, dans d'autres implémentations courantes de cette méthode, certains éléments textView, ImageView ou ImageButton doivent être renseignés par les valeurs de la liste [position], à l'aide de findViewById et ensuite .setText ou .setImageBitmap opérations. Ces opérations doivent venir après both créer une vue à partir de zéro par inflation et obtenir une vue existante, si elle n'est pas nulle.

Un autre bon exemple où cette solution est appliquée à BaseAdapter apparaît dans BaseAdapter, provoquant un arrêt de ListView lors du défilement

4
wiztrail

ListView réutilise les objets de vue lorsque vous faites défiler. Redéfinissez-vous la méthode getView? Vous devez vous assurer de définir chaque propriété pour chaque vue. N'assumez pas qu'elle se souviendra de ce que vous aviez auparavant. Si vous postez cette méthode, quelqu'un pourra probablement vous indiquer la partie incorrecte.

3
Cheryl Simon

J'ai un ListView, un AdapterView et un View (search_options) qui contient EditText et 3 Spinners. Les éléments ListView sont des copies multiples de la disposition (search_options), où l'utilisateur peut ajouter d'autres options dans ListView, puis cliquer sur rechercher pour envoyer une requête SQL construite selon les options de l'utilisateur.

J’ai trouvé que le mélange de convertView était indécis et j’ai ajouté une liste globale (myViews) à l’activité et je l’ai transmise à ArrayAdapter. Ensuite, dans ArrayAdapter (getView), j'y ajoute chaque vue nouvellement ajoutée (myViews).

Également sur getView au lieu de vérifier si convertView est null, je vérifie si la liste globale (myViews) a une vue sur la position sélectionnée .. Il résout totalement les problèmes après avoir perdu 3 jours en lisant Internet!

1- sur l'activité ajouter ceci:

Map<Integer, View> myViews = new HashMap<>();

puis transmettez-le à ArrayAdapter à l'aide du constructeur de l'adaptateur.

mSOAdapter = new SearchOptionsAdapter(getActivity(), resultStrs, myViews);

2- sur getView:

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    View view;
    ViewHolder viewHolder;

    if (!myViews.containsKey(position)) {
        viewHolder = new ViewHolder();
        LayoutInflater inflater = LayoutInflater.from(getContext());
        view = inflater.inflate(R.layout.search_options, parent, false);

        /// ...... YOUR CODE

        myViews.put(position, view);

        FontUtils.setCustomFontsIn(view, getContext().getAssets());

    }else {
        view = myViews.get(position);
    }

    return view;
}

Enfin plus d'objets à mélanger ...

0
Ibrahim