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

}

- EDIT:

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

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

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

Работает, но не так, как он должен работать.

Ответ 1

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

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

Для

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

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

Ответ 2

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

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

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

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

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

Ответ 3

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

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

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

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

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

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

Ответ 4

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

receiptsListView.invalidate().

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

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

Ответ 5

У меня была такая же проблема, используя 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);
}

Ответ 6

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

Вам нужно обновить адаптер:

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

myAutoComplete.setAdapter(myAutoCompleteAdapter);

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

Ответ 7

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

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

Ответ 8

class StudentAdapter extends BaseAdapter {
    ArrayList<LichHocDTO> studentList;

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

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

Ответ 9

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

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

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

}

Ответ 10

Если по какой-либо причине вы adapter.invaidate() этом потоке и задаетесь вопросом, почему в вашем случае отсутствуют методы adapter.invaidate() или adapter.clear() возможно, потому, что вы можете использовать RecyclerView.Adapter вместо BaseAdapter который используется BaseAdapter этот вопрос. Если очистка list или arraylist не решает вашу проблему, то может случиться так, что вы делаете два или более экземпляров adapter например:

Основная деятельность

...

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

...

а также
SomeFragment

...

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

...

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

SomeFragment

...

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

...

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