notifyDataSetChange не работает с пользовательским адаптером


126

Когда я заново заполняю свой 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;
    }

}

--РЕДАКТИРОВАТЬ:

найдено обходное решение

Просто чтобы иметь некоторый функциональный код, который я делаю сейчас:

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

Работает, но не так, как предполагается.


stackoverflow.com/a/4198569/2382964 , Привет, Джаспер, пожалуйста, обратитесь по этой ссылке ... это поможет вам.
Tushar Pandey

попробуйте другие методы, такие как notifyItemInserted, notifyItemRemoved и т. д.
Reejesh PK

Ответы:


333

Измените свой метод с

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вместо a Listof objects, что вы можете сделать для решения этой проблемы?
prom85

@ prom85 могут ли ArrayAdapters даже связываться с объектами, отличными от списков или массивов? Я не знала.
tolgap

он о a, BaseAdapterи этот адаптер не знает, к каким данным он привязан ... поэтому, если у меня есть настраиваемый объект и я использую настраиваемые функции этого объекта (например, custObject.getCount()и, custObject.getChildAt(int i)например), и я хочу обменять этот объект, notifyDataSetChangedне работает ... в любом случае, я думаю, что эта проблема никогда не возникает сArrayAdapter
prom85

3
Можете ли вы объяснить, почему первый метод не работает, а второй работает с BaseAdapter?
Tooroop

1
комментарии вроде «Спасибо» или «+1» не допускаются, но за некоторые ответы я очень люблю поблагодарить :)
Мухаммад Сакиб

24

У меня такая же проблема, и я это понимаю. Когда мы создаем адаптер и устанавливаем его на listview, listview будет указывать на объект где-то в памяти, который удерживает адаптер, данные в этом объекте будут отображаться в listview.

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

если мы снова создадим объект для адаптера с другими данными и notifydatasetchanged ():

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

это не повлияет на данные в списке, потому что список указывает на другой объект, этот объект ничего не знает о новом объекте в адаптере, а notifyDataSetChanged () ни на что не влияет. Поэтому мы должны изменить данные в объекте и избежать повторного создания нового объекта для адаптера.


10
вы воссоздаете объект адаптера, это неэффективно
Alezis 06

2
@Alezis Я думаю, это именно то, что имела в виду Нхан.
Нирадж Севани

17

Как я уже объяснил причины этой проблемы, а также способы ее решения в другом потоке ответов здесь . Тем не менее, я делюсь кратким изложением решения здесь.

Одна из основных причин, по которой у notifyDataSetChanged()вас не получится - это

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

При создании и добавлении нового списка в Adapter. Всегда следуйте этим рекомендациям:

  1. Инициализируйте объект arrayList, объявив его глобально.
  2. Добавьте список в адаптер напрямую, без проверки нулевых и пустых значений. Установите адаптер напрямую в список (не проверяйте никаких условий). Адаптер гарантирует вам, что где бы вы ни вносили изменения в данные, arrayListон позаботится об этом, но никогда не потеряет ссылку.
  3. Всегда изменяйте данные в самом arrayList (если ваши данные совершенно новые, чем вы можете вызвать, adapter.clear()и arrayList.clear()перед фактическим добавлением данных в список), но не устанавливайте адаптер, т.е. если новые данные заполняются arrayListтолько в adapter.notifyDataSetChanged()

Надеюсь это поможет.


1
Спасибо! Наконечник №2 помог.
Tiffany

4

Возможно, попробуйте обновить свой ListView:

receiptsListView.invalidate(),

РЕДАКТИРОВАТЬ: Еще одна мысль пришла мне в голову. На всякий случай попробуйте отключить кеш представления списка:

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

Как ни странно, моя последняя идея - переназначить адаптер для просмотра списка после изменения данных. Но я полагаю, вы тоже это пробовали.
Rafal Gałka

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напр .:

Основное занятие

...

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

...

и
SomeFragment

...

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

...

Если во втором случае вы ожидаете изменения в списке расширенных представлений в представлении ресайклера, этого не произойдет, поскольку во второй раз создается новый экземпляр, adapterкоторый не прикреплен к представлению ресайклера. Настройка notifyDataSetChangedвторого адаптера не изменит содержимое представления ресайсера. Для этого создайте новый экземпляр представления recycler в SomeFragment и прикрепите его к новому экземпляру адаптера.

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.