149 votes

notifyDataSetChange ne fonctionne pas à partir d'un adaptateur personnalisé

Quand je repeuplerai mon ListView si j'appelle une méthode spécifique de mon Adapter .

Problème :

Quand j'appelle updateReceiptsList de mon Adapter les données sont rafraîchies, mais mon ListView ne reflète pas le changement.

Question :

Pourquoi mon ListView montrer les nouvelles données quand j'appelle notifyDataSetChanged ?

Adaptateur :

public class ReceiptListAdapter extends BaseAdapter {

    public List<Receipt> receiptlist;
    private Context context;
    private LayoutInflater inflater;
    private DateHelpers dateH;

    public ReceiptListAdapter(Activity activity, Context mcontext, List<Receipt> rl) {
        context = mcontext;
        receiptlist = rl;
        Collections.reverse(receiptlist);
        inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        dateH = new DateHelpers();
    }

    @Override
    public int getCount() {
        try {
            int size = receiptlist.size();
            return size;
        } catch(NullPointerException ex) {
            return 0;
        }
    }

    public void updateReceiptsList(List<Receipt> newlist) {
        receiptlist = newlist;
        this.notifyDataSetChanged();
    }

    @Override
    public Receipt getItem(int i) {
        return receiptlist.get(i);
    }

    @Override
    public long getItemId(int i) {
        return receiptlist.get(i).getReceiptId() ;
    }

    private String getPuntenString(Receipt r) {
        if(r.getPoints().equals("1")) {
            return "1 punt";
        }
        return r.getPoints()+" punten";
    }

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

        final Receipt receipt = receiptlist.get(position);
        ReceiptViewHolder receiptviewholder;
        Typeface tf_hn = Typeface.createFromAsset(context.getAssets(), "helveticaneue.ttf");        
        Typeface tf_hn_bold = Typeface.createFromAsset(context.getAssets(), "helveticaneuebd.ttf");

        if (vi == null) { //convertview==null
            receiptviewholder = new ReceiptViewHolder();
            vi = inflater.inflate(R.layout.view_listitem_receipt, null);
            vi.setOnClickListener(null);
            vi.setOnLongClickListener(null);
            vi.setLongClickable(false);
            receiptviewholder.shop = (TextView) vi.findViewById(R.id.tv_listitemreceipt_shop);
            receiptviewholder.date = (TextView) vi.findViewById(R.id.tv_listitemreceipt_date);
            receiptviewholder.price = (TextView) vi.findViewById(R.id.tv_listitemreceipt_price);
            receiptviewholder.points = (TextView) vi.findViewById(R.id.tv_listitemreceipt_points);
            receiptviewholder.shop.setTypeface(tf_hn_bold);
            receiptviewholder.price.setTypeface(tf_hn_bold);
            vi.setTag(receiptviewholder);
        }else{//convertview is not null
            receiptviewholder = (ReceiptViewHolder)vi.getTag();
        }

        receiptviewholder.shop.setText(receipt.getShop());
        receiptviewholder.date.setText(dateH.timestampToDateString(Long.parseLong(receipt.getPurchaseDate())));
        receiptviewholder.price.setText("€ "+receipt.getPrice());
        receiptviewholder.points.setText(getPuntenString(receipt));

        vi.setClickable(false);
        return vi;
    }

    public static class ReceiptViewHolder {
        public TextView shop;
        public TextView date;
        public TextView price;
        public TextView points;
    }

    public Object getFilter() {
        // XXX Auto-generated method stub
        return null;
    }

}

--EDIT :

trouvé Solution de contournement

Juste pour avoir un code fonctionnel que je fais maintenant :

listview.setAdapter( new ReceiptListAdapter(activity,mcontext, -new dataset-);

Ça marche, mais pas comme c'est censé marcher.

368voto

tolgap Points 2392

Changez votre méthode de

public void updateReceiptsList(List<Receipt> newlist) {
    receiptlist = newlist;
    this.notifyDataSetChanged();
}

A

public void updateReceiptsList(List<Receipt> newlist) {
    receiptlist.clear();
    receiptlist.addAll(newlist);
    this.notifyDataSetChanged();
}

Vous gardez donc le même objet que votre DataSet dans votre adaptateur.

26voto

Nhan Tran Points 367

J'ai le même problème, et je m'en rends compte. Lorsque nous créons l'adaptateur et que nous le définissons pour listview, listview pointera vers l'objet quelque part dans la mémoire que l'adaptateur détient, les données de cet objet apparaîtront dans listview.

adapter = new CustomAdapter(data);
listview.setadapter(adapter);

si nous créons un objet pour l'adaptateur avec une autre donnée à nouveau et notifionsdatasetchanged() :

adapter = new CustomAdapter(anotherdata);
adapter.notifyDataSetChanged();

cela n'affectera pas les données dans le listview parce que la liste pointe vers un objet différent, cet objet ne sait rien du nouvel objet dans l'adaptateur, et notifyDataSetChanged() n'affecte rien. Nous devons donc changer les données dans l'objet et éviter de créer à nouveau un nouvel objet pour l'adaptateur.

21voto

Hissain Points 880

J'ai déjà expliqué les raisons de ce problème et la façon de le traiter dans un autre fil de réponse. Ici . Je partage quand même le résumé de la solution ici.

L'une des principales raisons notifyDataSetChanged() ne fonctionnera pas pour vous - est,

Votre adaptateur perd la référence à votre liste .

Lors de la création et de l'ajout d'une nouvelle liste à la Adapter . Suivez toujours ces directives :

  1. Initialiser le arrayList tout en le déclarant globalement.
  2. Ajouter la liste à l'adaptateur directement sans vérifier si elle est nulle ou vide. valeurs . Définir l'adaptateur à la liste directement (ne pas vérifier pour aucune condition). L'adaptateur vous garantit que chaque fois que vous apportez modifications aux données de la arrayList il s'en occupera, mais ne perdez jamais la référence.
  3. Modifiez toujours les données dans la liste de tableaux elle-même (si vos données sont complètement nouvelles, vous pouvez les modifier à votre guise). vous pouvez appeler adapter.clear() y arrayList.clear() avant avant d'ajouter des données à la liste) mais sans définir l'adaptateur. les nouvelles données sont remplies dans le arrayList que juste adapter.notifyDataSetChanged()

J'espère que cela vous aidera.

4voto

Rafal Gałka Points 336

Essayez peut-être de rafraîchir votre ListView :

receiptsListView.invalidate() .

EDIT : Une autre idée m'est venue à l'esprit. Pour mémoire, essayez de désactiver le cache de la vue en liste :

<ListView
    ...
    android:scrollingCache="false"
    android:cacheColorHint="@android:color/transparent"
    ... />

4voto

DZL Points 2080

J'ai eu le même problème en utilisant ListAdapter

J'ai laissé Android Studio implémenter des méthodes pour moi et voici ce que j'ai obtenu :

public class CustomAdapter implements ListAdapter {
    ...
    @Override
    public void registerDataSetObserver(DataSetObserver observer) {

    }

    @Override
    public void unregisterDataSetObserver(DataSetObserver observer) {

    }
    ...
}

Le problème est que ces méthodes n'appellent pas super de sorte que notifyDataSetChange n'est jamais appelé.

Soit vous supprimez ces surcharges manuellement, soit vous ajoutez des super appels et cela devrait fonctionner à nouveau.

@Override
public void registerDataSetObserver(DataSetObserver observer) {
    super.registerDataSetObserver(observer);
}

@Override
public void unregisterDataSetObserver(DataSetObserver observer) {
    super.unregisterDataSetObserver(observer);
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X