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

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

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


person Jasper    schedule 14.03.2013    source источник
comment
stackoverflow.com/a/4198569/2382964, Привет, Джаспер, пожалуйста, обратитесь по этой ссылке ... это поможет вам.   -  person Tushar Pandey    schedule 04.06.2013
comment
попробуйте другие методы, такие как notifyItemInserted, notifyItemRemoved и т. д.   -  person Reejesh PK    schedule 29.07.2019


Ответы (13)


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

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

To

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

Таким образом, вы сохраняете тот же объект, что и ваш DataSet, в вашем адаптере.

person tolgap    schedule 15.03.2013
comment
подумайте о том, чтобы иметь родительский объект, такой как ReceiptListObject, вместо List объектов, что вы можете сделать для решения этой проблемы? - person prom85; 10.02.2014
comment
@ prom85 могут ли ArrayAdapters даже связываться с объектами, отличными от списков или массивов? Я не знал. - person tolgap; 10.02.2014
comment
это о BaseAdapter, и этот адаптер не знает, к каким данным он привязан ... поэтому, если у меня есть настраиваемый объект и я использую настраиваемые функции этого объекта (например, custObject.getCount() и custObject.getChildAt(int i)), и я хочу обменять этот объект, notifyDataSetChanged не работает ... в любом случае, я думаю, что эта проблема никогда не возникает с ArrayAdapter - person prom85; 10.02.2014
comment
кстати, ты говоришь только о ArrayAdapter? Там ваш ответ имеет смысл ... Я бы понятия не имел, почему замена объекта списка должна вызывать проблемы в _2 _... Но я не могу заставить BaseAdapter работать должным образом, не перезагружая адаптер вместо вызова notifyDataSetChanged .. . × Комментарии можно редактировать только в течение 5 минут × Комментарии можно редактировать только в течение 5 минут × Комментарии можно редактировать только в течение 5 минут - person prom85; 10.02.2014
comment
Можете ли вы объяснить, почему первый метод не работает, а второй работает с BaseAdapter? - person Tooroop; 29.03.2014
comment
@tolgap этот вопрос был адресован вам :) Первое решение работало у меня какое-то время, но потом оно перестало работать, и мне пришлось изменить код, как во втором методе. Теперь все работает нормально. - person Tooroop; 29.03.2014
comment
@Tooroop Я не могу представить, чтобы первое решение работало, потому что все, что вам нужно сделать, это изменить ссылку на переменную receiptlist. BaseAdapter не знает об этом, поэтому notifyDataSetChanged() ничего не изменит. Вам следует поработать над исходной ссылкой List и удалить / добавить в нее элементы List. - person tolgap; 30.03.2014
comment
Как-то он не работает с моим статическим рэрайлистом. У меня есть 2 адаптера в первом. Это работает как шарм. Но во втором же адаптере он дает принудительное закрытие, говорит, что ArrayOutOfIndex и notifydatasetchanged не работают - person Dr. aNdRO; 21.11.2014
comment
@Tooroop Я использовал второй способ, очищая и добавляя новые элементы. Но изменение его на первый способ сработало для меня. Очень странно. - person Sarthak Majithia; 19.06.2015
comment
комментарии вроде «Спасибо» или «+1» не допускаются, но за некоторые ответы я очень хочу поблагодарить :) - person Muhammad Saqib; 24.03.2019
comment
Привет всем, я использую эти методы, которые вы упомянули, но у меня они не работают. Не могли бы вы проверить мой вопрос? stackoverflow .com / questions / 66827414 / - person stefanosn; 27.03.2021

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

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

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

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

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

person Nhan Tran    schedule 21.04.2014
comment
вы воссоздаете объект адаптера, это неэффективно - person Alezis; 06.02.2018
comment
@Alezis Я думаю, это именно то, что имела в виду Нхан. - person Neeraj Sewani; 30.05.2019
comment
Привет всем, я использую эти методы, которые вы упомянули, но у меня они не работают. Не могли бы вы проверить мой вопрос? stackoverflow .com / questions / 66827414 / - person stefanosn; 27.03.2021

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

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

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

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

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

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

person Sazzad Hissain Khan    schedule 01.08.2016
comment
Спасибо! Наконечник №2 помог. - person Tiffany; 13.07.2017
comment
Привет всем, я использую эти методы, которые вы упомянули, но у меня они не работают. Не могли бы вы проверить мой вопрос? stackoverflow .com / questions / 66827414 / - person stefanosn; 27.03.2021

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

receiptsListView.invalidate().

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

<ListView
    ...
    android:scrollingCache="false"
    android:cacheColorHint="@android:color/transparent"
    ... />
person Rafal Gałka    schedule 15.03.2013
comment
Как ни странно, моя последняя идея - переназначить адаптер для просмотра списка после изменения данных. Но я полагаю, вы тоже это пробовали. - person Rafal Gałka; 15.03.2013

У меня была такая же проблема с 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);
}
person Davor Zlotrg    schedule 22.12.2015
comment
Привет всем, я использую эти методы, которые вы упомянули, но у меня они не работают. Не могли бы вы проверить мой вопрос? stackoverflow .com / questions / 66827414 / - person stefanosn; 27.03.2021

class StudentAdapter extends BaseAdapter {
    ArrayList<LichHocDTO> studentList;

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

Можешь попробовать. Это работает для меня

person Dũng Phạm Tiến    schedule 27.01.2019

Если adapter установлен в AutoCompleteTextView, тогда notifyDataSetChanged() не работает.

Это нужно для обновления адаптера:

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

myAutoComplete.setAdapter(myAutoCompleteAdapter);

См .: http://android-er.blogspot.in/2012/10/autocompletetextview-with-dynamic.html

person Prabs    schedule 13.12.2017

Добавьте этот код

runOnUiThread(new Runnable() { public void run() {
               adapter = new CustomAdapter(anotherdata);
            adapter.notifyDataSetChanged();
            }
        });
person ShriKant A    schedule 22.03.2017

Если вы случайно попали в этот поток и задаетесь вопросом, почему методы 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();

...

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

SomeFragment

...

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

...

Хотя я не рекомендую создавать несколько экземпляров одного и того же адаптера и представления ресайклера.

person Neeraj Sewani    schedule 30.05.2019

В моем случае я просто забываю добавить в свой фрагмент mRecyclerView.setAdapter(adapter)

person Loic Jackotin    schedule 22.11.2020

Я совершил очень большую ошибку, когда настраивал адаптер RecyclerView перед инициализацией самого адаптера, как это.

    // Assuume oneOffJobTasksListRVAdapter is declared already 
    recyclerView.setAdapter(oneOffJobTasksListRVAdapter);        
    oneOffJobTasksListRVAdapter = new OneOffJobTasksListRVAdapter();

Переключение линий устранило мою проблему.

    oneOffJobTasksListRVAdapter = new OneOffJobTasksListRVAdapter();
    recyclerView.setAdapter(oneOffJobTasksListRVAdapter);        
person Muhammad Bilal ahmad    schedule 01.04.2021

У меня такая же проблема, только что закончил !!

ты должен сменить на

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

}
person 林權章    schedule 16.05.2019

Мой случай был другим, но может быть то же самое для других

для тех, кто все еще не смог найти решение и попробовал все, что описано выше, если вы используете адаптер внутри фрагмента, то причина, по которой он не работает 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<>();
}

person Farido mastr    schedule 24.04.2020