notifyDataSetChange tidak berfungsi dari adaptor khusus

126

Ketika saya mengisi kembali saya ListView, saya memanggil metode tertentu dari saya Adapter.

Masalah :

Ketika saya menelepon updateReceiptsListdari saya Adapter, data di-refresh, tetapi saya ListViewtidak mencerminkan perubahan.

Pertanyaan :

Mengapa saya tidak ListViewmenampilkan data baru saat saya menelepon notifyDataSetChanged?

Adaptor :

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:

menemukan Solusi

Hanya untuk memiliki beberapa kode fungsional yang saya lakukan sekarang:

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

Berhasil, tapi tidak bagaimana seharusnya bekerja.

Jasper
sumber
stackoverflow.com/a/4198569/2382964 , Hai Jasper, harap lihat tautan ini ... ini akan membantu Anda.
Tushar Pandey
coba metode lain seperti notifyItemInserted, notifyItemRemoved, dll ..
Reejesh PK

Jawaban:

334

Ubah metode Anda dari

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

Untuk

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

Jadi, Anda menyimpan objek yang sama dengan DataSet Anda di Adaptor.

tolgap
sumber
pertimbangkan untuk memiliki objek induk seperti ReceiptListObjectalih - alih Listobjek, apa yang dapat Anda lakukan untuk menyelesaikan masalah ini?
prom85
@ prom85 dapatkah ArrayAdapters mengikat Objek selain Daftar atau Array? Saya tidak tahu.
tolgap
Ini tentang a BaseAdapterdan adaptor ini tidak tahu data mana yang diikat ... jadi jika saya memiliki objek khusus dan menggunakan fungsi khusus objek ini (seperti custObject.getCount()dan custObject.getChildAt(int i)misalnya), dan saya ingin menukar objek ini, notifyDataSetChangedtidak berfungsi ... bagaimanapun, saya pikir masalah ini tidak pernah terjadi denganArrayAdapter
prom85
3
Bisakah Anda menjelaskan mengapa metode pertama tidak berfungsi dan yang kedua berfungsi dengan BaseAdapter?
Tooroop
1
komentar seperti Terima kasih atau +1 tidak diperbolehkan tetapi pada beberapa jawaban saya sangat ingin mengucapkan terima kasih :)
Muhammad Saqib
24

Saya memiliki masalah yang sama, dan saya menyadarinya. Saat kita membuat adaptor dan menyetelnya ke listview, listview akan mengarah ke objek di suatu tempat di memori yang dipegang adaptor, data di objek ini akan ditampilkan di listview.

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

jika kita membuat objek untuk adaptor dengan data lain lagi dan notifydatasetchanged ():

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

ini tidak akan memengaruhi data di listview karena daftar tersebut mengarah ke objek yang berbeda, objek ini tidak tahu apa-apa tentang objek baru di adaptor, dan notifyDataSetChanged () tidak memengaruhi apa pun. Jadi kita harus mengubah data dalam objek dan menghindari membuat objek baru lagi untuk adaptor

Nhan Tran
sumber
10
Anda membuat ulang objek adaptor, itu tidak efisien
Alezis
2
@Aleis Saya pikir itulah yang dimaksud Nhan.
Neeraj Sewani
17

Seperti yang telah saya jelaskan alasan di balik masalah ini dan juga cara menanganinya di utas jawaban yang berbeda Di Sini . Masih saya membagikan ringkasan solusi di sini.

Salah satu alasan utama notifyDataSetChanged()tidak berhasil untuk Anda - adalah,

Adaptor Anda kehilangan referensi ke daftar Anda .

Saat membuat dan menambahkan daftar baru ke Adapter. Selalu ikuti pedoman ini:

  1. Inisialisasi arrayListsambil mendeklarasikannya secara global.
  2. Tambahkan List ke adaptor secara langsung tanpa memeriksa nilai null dan kosong. Setel adaptor ke daftar secara langsung (jangan periksa kondisi apa pun). Adaptor menjamin Anda bahwa di mana pun Anda membuat perubahan pada datanya, arrayListitu akan mengurusnya, tetapi tidak pernah kehilangan referensi.
  3. Selalu ubah data di arrayList itu sendiri (jika data Anda benar-benar baru daripada yang dapat Anda panggil adapter.clear()dan arrayList.clear()sebelum benar-benar menambahkan data ke daftar) tetapi jangan setel adaptor, yaitu Jika data baru diisi di arrayListthan just adapter.notifyDataSetChanged()

Semoga ini membantu.

Sazzad Hissain Khan
sumber
1
Terima kasih! Tip di # 2 membantu.
Cheruby
4

Mungkin mencoba menyegarkan ListView Anda:

receiptsListView.invalidate().

EDIT: Pikiran lain muncul di benak saya. Sekadar catatan, coba nonaktifkan cache tampilan daftar:

<ListView
    ...
    android:scrollingCache="false"
    android:cacheColorHint="@android:color/transparent"
    ... />
Rafal Gałka
sumber
Aneh, ide terakhir saya adalah menetapkan kembali adaptor ke tampilan daftar setelah data diubah. Tapi saya rasa Anda sudah mencobanya juga.
Rafal Gałka
3

Saya memiliki masalah yang sama saat menggunakan ListAdapter

Saya membiarkan Android Studio mengimplementasikan metode untuk saya dan inilah yang saya dapatkan:

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

    }

    @Override
    public void unregisterDataSetObserver(DataSetObserver observer) {

    }
    ...
}

Masalahnya adalah bahwa metode ini tidak memanggil superimplementasi sehingga notifyDataSetChangetidak pernah dipanggil.

Hapus penggantian ini secara manual atau tambahkan panggilan super dan ini akan berfungsi kembali.

@Override
public void registerDataSetObserver(DataSetObserver observer) {
    super.registerDataSetObserver(observer);
}

@Override
public void unregisterDataSetObserver(DataSetObserver observer) {
    super.unregisterDataSetObserver(observer);
}
Davor Zlotrg
sumber
2
class StudentAdapter extends BaseAdapter {
    ArrayList<LichHocDTO> studentList;

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

Anda dapat mencoba. Ini bekerja untuk saya

Dũng Phạm Tiến
sumber
1

Jika kebetulan Anda mendarat di utas ini dan bertanya-tanya mengapa adapter.invaidate()atau adapter.clear()metode tidak ada dalam kasus Anda, maka mungkin karena Anda mungkin menggunakan RecyclerView.Adapteralih-alih BaseAdapteryang digunakan oleh penanya pertanyaan ini. Jika menyelesaikan listatau arraylisttidak menyelesaikan masalah Anda maka mungkin saja Anda membuat dua atau lebih contoh fileadapter untuk mantan .:

Aktifitas utama

...

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

...

dan
SomeFragment

...

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

...

Jika dalam kasus kedua Anda mengharapkan perubahan dalam daftar tampilan yang digelembungkan dalam tampilan pendaur ulang, maka itu tidak akan terjadi karena untuk kedua kalinya instance baru adapterdibuat yang tidak dilampirkan ke tampilan pendaur ulang. Pengaturan notifyDataSetChangeddi adaptor kedua tidak akan mengubah konten tampilan pendaur ulang. Untuk itu buatlah contoh baru dari tampilan pendaur ulang di SomeFragment dan lampirkan ke contoh baru dari adaptor.

SomeFragment

...

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

...

Meskipun, saya tidak menyarankan membuat beberapa contoh tampilan adaptor dan pendaur ulang yang sama.

Neeraj Sewani
sumber
0

Tambahkan kode ini

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

Saya memiliki masalah yang sama tetapi saya baru saja menyelesaikannya !!

Anda harus berubah menjadi

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

}
林 權 章
sumber
0

Kasus saya berbeda tetapi mungkin kasus yang sama untuk orang lain

bagi mereka yang masih tidak dapat menemukan solusi dan mencoba semua hal di atas, jika Anda menggunakan adaptor di dalam fragmen maka alasannya tidak berfungsi fragment could be recreating so the adapter is recreating everytime the fragment recreate

Anda harus memverifikasi apakah adaptor dan daftar objek adalah null sebelum menginisialisasi

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

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

Farido mastr
sumber