notifyDataSetChange не працює від користувацького адаптера


125

Коли я переселяю свою ListView, я називаю конкретний метод із мого Adapter.

Проблема :

Коли я дзвоню updateReceiptsListзі свого телефону Adapter, дані оновлюються, але мої зміни ListViewне відображають.

Питання :

Чому ListViewпід час дзвінка не відображаються нові дані notifyDataSetChanged?

Адаптер :

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:

знайдено обхідний шлях

Просто, щоб мати якийсь функціональний код, я зараз роблю:

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

Працює, але не так, як це повинно працювати.


stackoverflow.com/a/4198569/2382964 , Привіт Джаспер, будь ласка, перейди за цим посиланням ... це допоможе тобі.
Тушар Пандей

спробуйте інші методи, такі як notifyItemInserted, notifyItemRemoved тощо.
Reejesh PK

Відповіді:


334

Змініть свій метод на

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

До

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

Таким чином, ви зберігаєте той самий об’єкт, що і ваш DataSet, у своєму адаптері.


Подумайте, чи є батьківський об'єкт, як ReceiptListObjectзамість Listоб'єктів, що ви можете зробити для вирішення цієї проблеми?
prom85

@ prom85 може ArrayAdapters навіть прив'язуватися до інших об'єктів, крім списків чи масивів? Я не знав.
tolgap

це про BaseAdapterі це адаптер не знає, які дані він переплетений ... так що якщо у мене є призначений для користувача об'єкт і використовувати призначені для користувача функції цього об'єкта (наприклад , custObject.getCount()і custObject.getChildAt(int i), наприклад), і я хочу , щоб обміняти цей об'єкт, notifyDataSetChangedне працюю ... так чи інакше, я думаю, що ця проблема ніколи не виникає зArrayAdapter
prom85

3
Чи можете ви пояснити, чому перший метод не працює, а другий працює з BaseAdapter?
Tooroop

1
коментарі, такі як подяки або +1, заборонені, але за деякі відповіді я дуже люблю подякувати :)
Muhammad

24

У мене така ж проблема, і я це усвідомлюю. Коли ми створимо адаптер і встановимо його для перегляду listlist, listview буде вказувати на об'єкт десь у пам’яті, який утримує адаптер, дані цього об’єкта відображатимуться у перегляді списку.

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

якщо ми знову створимо об’єкт для адаптера з іншими даними та сповістимо налаштування змінити ():

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

це не вплине на дані в listview, оскільки список вказує на інший об'єкт, цей об'єкт нічого не знає про новий об'єкт у адаптері, і notifyDataSetChanged () нічого не впливає. Тому нам слід змінювати дані в об'єкті і уникати створення нового об'єкта для адаптера


10
ви відтворюєте об’єкт адаптера, це не ефективно
Alezis

2
@Alezis Я думаю, що саме це означало Нхан.
Нерадж Севані

17

Як я вже пояснив причини цього питання, а також як впоратися з ним в іншому потоці відповідей Тут . Ще я ділюсь резюме рішення тут.

Одна з головних причин notifyDataSetChanged()не працює для вас - це,

Ваш адаптер втрачає посилання на ваш список .

Під час створення та додавання нового списку до Adapter. Завжди дотримуйтесь цих вказівок:

  1. Ініціалізуйте arrayListчас, декларуючи це глобально.
  2. Додайте список до адаптера безпосередньо, перевіряючи наявність нульових та порожніх значень. Встановіть адаптер безпосередньо до списку (не перевіряйте будь-які умови). Адаптер гарантує вам, що де ви внесете зміни до даних, arrayListвін подбає про це, але ніколи не втрачає посилання.
  3. Завжди змінюйте дані в самому масиві arrayList (якщо ваші дані абсолютно нові, ніж ви можете зателефонувати, adapter.clear()і arrayList.clear()до того, як фактично додавати дані до списку), але не встановлюйте адаптер, тобто якщо нові дані заповнюються у, arrayListніж просто adapter.notifyDataSetChanged()

Сподіваюся, це допомагає.


1
Дякую! Підказка у №2 допомогла.
Черубі

4

Можливо, спробуйте оновити список ListView:

receiptsListView.invalidate().

EDIT: Ще одна думка впала мені в голову. Тільки для запиту спробуйте вимкнути кеш перегляду списку:

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

Дивно, моя остання ідея - перепризначити адаптер для перегляду списку після зміни даних. Але я вважаю, що ви вже пробували це.
Рафал Галька

3

У мене була така ж проблема з використанням ListAdapter

Я дозволяю Android Studio реалізовувати для мене методи, і ось що я отримав:

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

    }

    @Override
    public void unregisterDataSetObserver(DataSetObserver observer) {

    }
    ...
}

Проблема полягає в тому, що ці методи не викликають superреалізації, тому notifyDataSetChangeніколи не викликаються.

Видаліть ці скасування вручну або додайте супердзвінки, і це повинно працювати знову.

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

Ви можете спробувати. Це працює для мене



1

Якщо ви випадково приземлилися на цю тему і цікавитеся, чому adapter.invaidate()або adapter.clear()методів немає у вашому випадку, можливо, тому, що ви, можливо, використовуєте RecyclerView.Adapterзамість цього, BaseAdapterякий використовується запитувачем цього питання. Якщо усунути listчи arraylistне вирішити проблему, то може статися, що ви робите два або більше примірників з adapterнапр.:

MainActivity

...

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

...

і
SomeFragment

...

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

...

Якщо у другому випадку ви очікуєте зміни списку завищених подань у перегляді рециркулятора, то це не відбудеться, як у другий раз буде створено новий екземпляр, adapterякий не додається до подання рециркулятора. Встановлення notifyDataSetChangedдругого адаптера не змінить вміст перегляду перетворювача. Для цього зробіть новий екземпляр подання переробника в SomeFragment і додайте його до нового примірника адаптера.

ДеякіФрагмент

...

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

...

Хоча я не рекомендую робити кілька примірників одного і того ж перегляду адаптера та переробника.


0

Додайте цей код

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

0

У мене така ж проблема, але я її просто закінчив !!

ви повинні змінитись на

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

У мене справа була іншою, але це може бути той самий випадок і для інших

для тих, хто все ще не міг знайти рішення і спробував усе вище, якщо ви використовуєте адаптер всередині фрагмента, то причина не працює fragment could be recreating so the adapter is recreating everytime the fragment recreate

Ви повинні переконатися, що адаптер та список об'єктів є нульовими перед ініціалізацією

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

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.