notifyDataSetChange ne fonctionne pas à partir de l'adaptateur personnalisé

126

Lorsque je repeuple mon ListView, j'appelle une méthode spécifique de mon Adapter.

Problème :

Lorsque j'appelle updateReceiptsListdepuis mon Adapter, les données sont actualisées, mais mon ListViewne reflète pas le changement.

Question :

Pourquoi mon ListViewaffiche-t-il les nouvelles données lorsque 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;
    }

}

--ÉDITER:

solution de contournement trouvée

Juste pour avoir du code fonctionnel que je fais maintenant:

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

Fonctionne, mais pas comment il est censé fonctionner.

Jaspe
la source
stackoverflow.com/a/4198569/2382964 , Salut Jasper, veuillez vous référer à ce lien ... cela vous aidera.
Tushar Pandey
essayez d'autres méthodes comme notifyItemInserted, notifyItemRemoved, etc.
Reejesh PK

Réponses:

334

Changez votre méthode de

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

À

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

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

Tolgap
la source
envisagez d'avoir un objet parent comme ReceiptListObjectau lieu d'un Listobjet, que pouvez-vous faire alors pour résoudre ce problème?
prom85
@ prom85 les ArrayAdapters peuvent-ils même se lier à des objets autres que des listes ou des tableaux? Je ne savais pas.
tolgap
il s'agit d'un BaseAdapteret cet adaptateur ne sait pas à quelles données il est lié ... donc si j'ai un objet personnalisé et que j'utilise des fonctions personnalisées de cet objet (comme custObject.getCount()et custObject.getChildAt(int i)par exemple), et que je souhaite échanger cet objet, cela notifyDataSetChangedne fonctionne pas ... de toute façon, je pense que ce problème ne se produit jamais avec unArrayAdapter
prom85
3
Pouvez-vous expliquer pourquoi la première méthode ne fonctionne pas et la seconde fonctionne avec un BaseAdapter?
Tooroop
1
les commentaires comme Merci ou +1 ne sont pas autorisés mais sur certaines réponses, j'aime vraiment remercier :)
Muhammad Saqib
24

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

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

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

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

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

Nhan Tran
la source
10
vous
recréez
2
@Alezis Je pense que c'est exactement ce que Nhan voulait dire.
Neeraj Sewani
17

Comme je l'ai déjà expliqué les raisons de ce problème et comment le gérer dans un fil de réponse différent ici . Je partage toujours 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 au fichier Adapter. Suivez toujours ces directives:

  1. Initialisez le arrayListwhile en le déclarant globalement.
  2. Ajoutez directement la liste à l'adaptateur sans vérifier les valeurs nulles et vides. Définissez directement l'adaptateur dans la liste (ne vérifiez aucune condition). L'adaptateur vous garantit que partout où vous apportez des modifications aux données du, arrayListil s'en chargera, mais ne perdra jamais la référence.
  3. Toujours modifier les données du arrayList lui - même (si vos données est tout à fait nouvelle que vous pouvez appeler adapter.clear()et arrayList.clear()avant d' ajouter des données réellement à la liste) , mais ne définissez pas l' adaptateur IE Si les nouvelles données est renseigné dans le arrayListpas seulement adapter.notifyDataSetChanged()

J'espère que cela t'aides.

Sazzad Hissain Khan
la source
1
Merci! Le conseil n ° 2 a aidé.
Cheruby
4

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

receiptsListView.invalidate().

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

<ListView
    ...
    android:scrollingCache="false"
    android:cacheColorHint="@android:color/transparent"
    ... />
Rafal Gałka
la source
Bizarre, ma dernière idée est de réaffecter l'adaptateur à la vue de liste après la modification des données. Mais je suppose que vous avez déjà essayé cela aussi.
Rafal Gałka
3

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

Je laisse 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 d' superimplémentations et notifyDataSetChangene sont donc jamais appelées.

Supprimez ces remplacements manuellement ou 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);
}
Davor Zlotrg
la source
2
class StudentAdapter extends BaseAdapter {
    ArrayList<LichHocDTO> studentList;

    private void capNhatDuLieu(ArrayList<LichHocDTO> list){
        this.studentList.clear();
        this.studentList.addAll(list);
        this.notifyDataSetChanged();
    }
}

Tu peux essayer. Ça marche pour moi

Dũng Phạm Tiến
la source
1

Si adapterest défini sur AutoCompleteTextViewalors notifyDataSetChanged()ne fonctionne pas.

Besoin de ceci pour mettre à jour l'adaptateur:

myAutoCompleteAdapter = new ArrayAdapter<String>(MainActivity.this, 
        android.R.layout.simple_dropdown_item_1line, myList);

myAutoComplete.setAdapter(myAutoCompleteAdapter);

Reportez-vous: http://android-er.blogspot.in/2012/10/autocompletetextview-with-dynamic.html

Prabes
la source
1

Si, par hasard, vous avez atterri sur ce fil et vous vous demandez pourquoi adapter.invaidate()ou des adapter.clear()méthodes ne sont pas présentes dans votre cas, peut-être parce que vous utilisez peut-être à la RecyclerView.Adapterplace de BaseAdapterce qui est utilisé par le demandeur de cette question. Si vous effacez listou arraylistne résolvez pas votre problème, il se peut que vous créiez au moins deux instances du adapterpar exemple:

Activité principale

...

adapter = new CustomAdapter(list);
adapter.notifyDataSetChanged();
recyclerView.setAdapter(adapter);

...

et
SomeFragment

...

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

...

Si, dans le second cas, vous attendez un changement dans la liste des vues gonflées dans la vue du recycleur, cela ne se produira pas comme dans la deuxième fois, une nouvelle instance du adapterest créée qui n'est pas attachée à la vue du recycleur. La configuration notifyDataSetChangeddu deuxième adaptateur ne changera pas le contenu de la vue du récupérateur. Pour cela, créez une nouvelle instance de la vue recycleur dans SomeFragment et attachez-la à la nouvelle instance de l'adaptateur.

Certains Fragment

...

recyclerView = new RecyclerView();
adapter = new CustomAdapter();
recyclerView.setAdapter(adapter);

...

Cependant, je ne recommande pas de créer plusieurs instances de la même vue d'adaptateur et de recycleur.

Neeraj Sewani
la source
0

Ajoutez ce code

runOnUiThread(new Runnable() { public void run() {
               adapter = new CustomAdapter(anotherdata);
            adapter.notifyDataSetChanged();
            }
        });
ShriKant A
la source
0

J'ai le même problème mais je viens de le terminer !!

tu devrais changer pour

public class ReceiptListAdapter extends BaseAdapter {

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

    public ReceiptListAdapter(Activity activity, Context mcontext, List<Receipt> rl) {
        context = mcontext;
        receiptlist = rl;
        receiptviewlist = new ArrayList<>();
        receiptviewlist.clear();
        for(int i = 0; i < receiptlist.size(); i++){
          ReceiptViewHolder receiptviewholder = new ReceiptViewHolder();
          receiptviewlist.add(receiptviewholder);
        }
        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 receiptviewholder = receiptviewlist.get(position);
            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;
    }

}
林 權 章
la source
0

Mon cas était différent mais ça pourrait être le même cas pour d'autres

pour ceux qui n'ont toujours pas trouvé de solution et ont tout essayé ci-dessus, si vous utilisez l'adaptateur à l'intérieur du fragment, la raison pour laquelle cela ne fonctionne pas fragment could be recreating so the adapter is recreating everytime the fragment recreate

vous devez vérifier si l'adaptateur et la liste des objets sont nuls avant de l'initialiser

if(adapter == null){
  adapter = new CustomListAdapter(...);
}
...

if(objects == null){
  objects = new ArrayList<>();
}

Farido mastr
la source