notifyDataSetChange não está funcionando no adaptador customizado


125

Quando repovoo meu ListView, chamo um método específico do meu Adapter.

Problema :

Quando ligo , updateReceiptsListmeus Adapterdados são atualizados, mas meus ListViewnão refletem a alteração.

Pergunta :

Por que meu ListViewshow não mostra os novos dados quando ligo notifyDataSetChanged?

Adaptador :

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;
    }

}

--EDITAR:

Solução alternativa encontrada

Apenas para ter algum código funcional que eu faço agora:

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

Funciona, mas não como deve funcionar.


stackoverflow.com/a/4198569/2382964 , Olá Jasper, consulte este link ... isso irá ajudá-lo.
Tushar Pandey

tentar outros métodos, como notifyItemInserted, notifyItemRemoved, etc ..
Reejesh PK

Respostas:


334

Mude o seu método de

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

Para

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

Portanto, você mantém o mesmo objeto que seu DataSet no seu adaptador.


considere ter um objeto pai como em ReceiptListObjectvez de um Listde objetos, o que você pode fazer para resolver esse problema?
prom85

@ prom85 o ArrayAdapters pode vincular-se a objetos diferentes de Lists ou Arrays? Eu não sabia.
tolgap

trata-se de um BaseAdaptere este adaptador não sabe a quais dados ele está vinculado ... então, se eu tiver um objeto personalizado e usar funções personalizadas desse objeto (como custObject.getCount()e custObject.getChildAt(int i)por exemplo) e quiser trocar esse objeto, notifyDataSetChangednão está funcionando ... de qualquer forma, acho que esse problema nunca ocorre com umArrayAdapter
prom85

3
Talvez você possa explicar por que o primeiro método não funciona e o segundo funciona com um BaseAdapter?
Tooroop 29/03

1
comentários como Graças ou +1 não são permitidos, mas em algumas respostas que eu realmente gostaria de dar um agradecimento :)
Muhammad Saqib

24

Eu tenho o mesmo problema e percebo isso. Quando criamos o adaptador e o configuramos para listview, o listview apontará para um objeto em algum lugar na memória que o adaptador retém, os dados nesse objeto serão mostrados no listview.

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

se criarmos um objeto para o adaptador com outros dados novamente e notifydatasetchanged ():

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

isso não afetará os dados na exibição de lista porque a lista está apontando para um objeto diferente, esse objeto não sabe nada sobre o novo objeto no adaptador e notifyDataSetChanged () não afeta nada. Portanto, devemos alterar os dados no objeto e evitar criar um novo objeto novamente para o adaptador


10
você está recriando o objeto adaptador, ele não é eficiente
Alezis

2
@ Alexis eu acho que é exatamente isso que Nhan quis dizer.
Neeraj Sewani

17

Como já expliquei as razões por trás desse problema e também como lidar com isso em um segmento de resposta diferente, aqui . Ainda estou compartilhando o resumo da solução aqui.

Um dos principais motivos notifyDataSetChanged()não funcionará para você - é,

Seu adaptador perde referência à sua lista .

Ao criar e adicionar uma nova lista ao arquivo Adapter. Sempre siga estas diretrizes:

  1. Inicialize o arrayListdeclarando globalmente.
  2. Inclua a lista no adaptador diretamente sem verificar valores nulos e vazios. Defina o adaptador diretamente para a lista (não verifique nenhuma condição). O adaptador garante que, onde quer que você faça alterações nos dados do, arrayListele cuidará dele, mas nunca perderá a referência.
  3. Sempre modificar os dados no próprio arrayList (se os seus dados é completamente novo do que você pode ligar adapter.clear()e arrayList.clear()antes de realmente adicionar dados à lista), mas não definir o adaptador ou seja, se os novos dados são preenchidos no arrayListque apenas adapter.notifyDataSetChanged()

Espero que isto ajude.


1
Obrigado! A dica no # 2 ajudou.
Cheruby

4

Talvez tente atualizar seu ListView:

receiptsListView.invalidate().

Edição: Outro pensamento veio à minha mente. Apenas para o registro, tente desativar o cache da exibição de lista:

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

Estranho, minha última idéia é reatribuir o adaptador à exibição de lista após a alteração dos dados. Mas suponho que você já tentou isso também.
Rafal Gałka 15/03

3

Eu tive o mesmo problema usando ListAdapter

Eu deixei o Android Studio implementar métodos para mim e é isso que eu tenho:

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

    }

    @Override
    public void unregisterDataSetObserver(DataSetObserver observer) {

    }
    ...
}

O problema é que esses métodos não chamam superimplementações, portanto notifyDataSetChangenunca são chamados.

Remova essas substituições manualmente ou adicione super chamadas e deve funcionar novamente.

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

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

2
class StudentAdapter extends BaseAdapter {
    ArrayList<LichHocDTO> studentList;

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

Podes tentar. Funciona para mim



1

Se por acaso você aterrissou nesse tópico e se perguntou por que adapter.invaidate()ou os adapter.clear()métodos não estão presentes no seu caso, talvez porque você possa estar usando, e RecyclerView.Adapternão o BaseAdapterque é usado pelo autor da pergunta. Se limpar listou arraylistnão resolver o seu problema, pode acontecer que você esteja executando duas ou mais instâncias do exemplo,adapter por exemplo:

Atividade principal

...

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

...

e
SomeFragment

...

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

...

Se, no segundo caso, você estiver esperando uma alteração na lista de visualizações infladas no modo de exibição do reciclador, isso não acontecerá, pois na segunda vez em que uma nova instância do adapteré criada, que não está anexada ao modo de exibição do reciclador. A configuração notifyDataSetChangedno segundo adaptador não alterará o conteúdo da exibição da recicla. Para isso, crie uma nova instância da visualização do reciclador em SomeFragment e anexe-a à nova instância do adaptador.

SomeFragment

...

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

...

No entanto, não recomendo criar várias instâncias da mesma exibição de adaptador e reciclador.


0

Adicione este código

runOnUiThread(new Runnable() { public void run() {
               adapter = new CustomAdapter(anotherdata);
            adapter.notifyDataSetChanged();
            }
        });

0

Eu tenho o mesmo problema, mas acabei de terminar !!

você deveria mudar para

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;
    }

}

0

Meu caso foi diferente, mas pode ser o mesmo para outros

para aqueles que ainda não conseguiram encontrar uma solução e tentaram tudo acima, se você estiver usando o adaptador dentro do fragmento, o motivo pelo qual ele não está funcionando fragment could be recreating so the adapter is recreating everytime the fragment recreate

você deve verificar se o adaptador e a lista de objetos são nulos antes de inicializar

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

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

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.