notifyDataSetChange nie działa z niestandardowej karty


126

Kiedy ponownie zapełniam mój ListView, wywołuję określoną metodę z pliku my Adapter.

Problem :

Kiedy dzwonię updateReceiptsListz mojego Adapter, dane są odświeżane, ale mój ListViewnie odzwierciedla zmiany.

Pytanie 30 :

Dlaczego nie ListViewpokazuję nowych danych, kiedy dzwonię notifyDataSetChanged?

Adapter :

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

}

--EDYTOWAĆ:

znaleziono obejście

Żeby mieć jakiś funkcjonalny kod, robię teraz:

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

Działa, ale nie tak, jak ma działać.


stackoverflow.com/a/4198569/2382964 , Cześć Jasper, skorzystaj z tego linku ... to ci pomoże.
Tushar Pandey

spróbować innych metod, takich jak notifyItemInserted, notifyItemRemoved, etc ..
Reejesh PK

Odpowiedzi:


333

Zmień metodę z

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

Do

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

Więc zachowujesz ten sam obiekt co zestaw danych w adapterze.


rozważ posiadanie obiektu nadrzędnego, takiego jak ReceiptListObjectzamiast Listobiektów a, co możesz wtedy zrobić, aby rozwiązać ten problem?
prom85

@ prom85 czy ArrayAdapters może nawet wiązać się z obiektami innymi niż listy lub tablice? Nie wiedziałem.
tolgap

chodzi o a BaseAdapteri ten adapter nie wie, z którymi danymi jest powiązany ... więc jeśli mam niestandardowy obiekt i używam niestandardowych funkcji tego obiektu (jak custObject.getCount()i custObject.getChildAt(int i)na przykład), a chcę wymienić ten obiekt, notifyDataSetChangednie działa ... w każdym razie myślę, że ten problem nigdy nie występuje w przypadkuArrayAdapter
prom85

3
Czy możesz wyjaśnić, dlaczego pierwsza metoda nie działa, a druga działa z adapterem BaseAdapter?
Tooroop

1
komentarze typu Dziękuję lub +1 nie są dozwolone, ale w przypadku niektórych odpowiedzi bardzo lubię podziękować :)
Muhammad Saqib

24

Mam ten sam problem i zdaję sobie z tego sprawę. Kiedy utworzymy adapter i ustawimy go na listview, listview wskaże obiekt gdzieś w pamięci, który zawiera adapter, dane w tym obiekcie będą wyświetlane w widoku listy.

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

jeśli ponownie utworzymy obiekt dla adaptera z innymi danymi i notifydatasetchanged ():

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

nie wpłynie to na dane w widoku listy, ponieważ lista wskazuje na inny obiekt, ten obiekt nie wie nic o nowym obiekcie w adapterze, a notifyDataSetChanged () nie wpływa na nic. Dlatego powinniśmy zmienić dane w obiekcie i unikać ponownego tworzenia nowego obiektu dla adaptera


10
odtwarzasz obiekt adaptera, nie jest to wydajne
Alezis,

2
@Alezis Myślę, że to właśnie miał na myśli Nhan.
Neeraj Sewani

17

Jak już wyjaśniłem przyczyny tego problemu, a także jak sobie z nim poradzić w innym wątku odpowiedzi tutaj . Wciąż udostępniam tutaj podsumowanie rozwiązania.

Jednym z głównych powodów notifyDataSetChanged(), dla których nie zadziała - jest

Twoja karta traci odniesienie do listy .

Podczas tworzenia i dodawania nowej listy do Adapter. Zawsze przestrzegaj następujących wskazówek:

  1. Zainicjuj arrayList, deklarując to globalnie.
  2. Dodaj Listę bezpośrednio do adaptera bez sprawdzania wartości null i pustych. Ustaw adapter bezpośrednio na liście (nie sprawdzaj żadnego warunku). Adapter gwarantuje, że gdziekolwiek wprowadzisz zmiany w danych arrayList, zajmie się tym, ale nigdy nie zgubi referencji.
  3. Zawsze modyfikuj dane w samej arrayList (jeśli twoje dane są zupełnie nowe niż możesz wywołać adapter.clear()i arrayList.clear()przed faktycznym dodaniem danych do listy) ale nie ustawiaj adaptera, tj. Jeśli nowe dane są wypełnione w arrayListniż tylko adapter.notifyDataSetChanged()

Mam nadzieję że to pomoże.


1
Dzięki! Wskazówka z numeru 2 pomogła.
Cheruby

4

Może spróbuj odświeżyć ListView:

receiptsListView.invalidate().

EDYCJA: Kolejna myśl przyszła mi do głowy. Dla przypomnienia, spróbuj wyłączyć pamięć podręczną widoku listy:

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

Dziwne, moim ostatnim pomysłem jest ponowne przypisanie adaptera do widoku listy po zmianie danych. Ale przypuszczam, że już tego próbowałeś.
Rafał Gałka

3

Miałem ten sam problem podczas używania ListAdapter

Pozwoliłem Android Studio zaimplementować metody za mnie i oto co otrzymałem:

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

    }

    @Override
    public void unregisterDataSetObserver(DataSetObserver observer) {

    }
    ...
}

Problem polega na tym, że metody te nie wywołują superimplementacji, więc notifyDataSetChangenigdy nie są wywoływane.

Usuń te zastąpienia ręcznie lub dodaj super wywołania i powinno działać ponownie.

@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();
    }
}

Możesz spróbować. To działa dla mnie



1

Jeśli przypadkiem trafiłeś na ten wątek i zastanawiałeś się, dlaczego adapter.invaidate()lub adapter.clear()metody nie są obecne w twoim przypadku, może dlatego, że możesz użyć RecyclerView.Adapterzamiast tego, BaseAdapterktórego używa osoba zadająca to pytanie. Jeśli wyczyszczenie listlub arraylistnie rozwiązywanie problemu wtedy może się zdarzyć, że robisz dwa lub więcej wystąpień Spośród adapterex .:

Główna aktywność

...

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

...

i
SomeFragment

...

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

...

Jeśli w drugim przypadku spodziewasz się zmiany na liście zawyżonych widoków w widoku recyklera, to się to nie stanie, ponieważ za drugim razem zostanie utworzona nowa instancja elementu, adapterktóra nie jest dołączona do widoku recyklera. Ustawienie notifyDataSetChangedw drugim adapterze nie zmieni zawartości widoku recyklera. W tym celu utwórz nową instancję widoku recyklera w SomeFragment i dołącz ją do nowej instancji adaptera.

SomeFragment

...

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

...

Chociaż nie polecam tworzenia wielu instancji tego samego adaptera i widoku recyklera.


0

Dodaj ten kod

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

0

Mam ten sam problem, ale właśnie go skończyłem !!

powinieneś zmienić na

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

Mój przypadek był inny, ale może być tak samo dla innych

dla tych, którzy nadal nie mogli znaleźć rozwiązania i próbowali wszystkiego powyżej, jeśli używasz adaptera wewnątrz fragmentu, to powód, dla którego nie działa fragment could be recreating so the adapter is recreating everytime the fragment recreate

przed zainicjowaniem należy sprawdzić, czy lista adapterów i obiektów ma wartość NULL

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

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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.