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

}

-編集:

回避策が見つかりました

ちょうど私が今やっているいくつかの機能的なコードを持つために:

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

動作しますが、想定されている動作ではありません。


stackoverflow.com/a/4198569/2382964、こんにちはJasperはこのリンクを参照してください...これが役立ちます。
Tushar Pandey 2013

など。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 2014

@ prom85 ArrayAdaptersは、リストや配列以外のオブジェクトにもバインドできますか?知りませんでした。
tolgap 2014

それはについてですBaseAdapterと、このアダプタは、それがバインドされたデータには知らない...ので、私は(のように、このオブジェクトのカスタムオブジェクトと使用カスタム関数を使用している場合custObject.getCount()と、custObject.getChildAt(int i)例えば)、そして私は、このオブジェクトを交換したい、notifyDataSetChanged働いていません...とにかく、私はこの問題は決して発生しないと思いますArrayAdapter
prom85

3
最初の方法が機能せず、2番目の方法がBaseAdapterで機能する理由を説明できますか?
Tooroop 14年

1
感謝や+1のようなコメントは許可されていませんが、一部の回答では感謝を述べたいと思います:)
Muhammad Saqib

24

私は同じ問題を抱えており、私はそれを認識しています。アダプターを作成してそれをリストビューに設定すると、リストビューはアダプターが保持するメモリ内のどこかのオブジェクトを指し、このオブジェクトのデータはリストビューに表示されます。

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

別のデータを使用してアダプタのオブジェクトを再度作成し、notifydatasetchanged()を実行した場合:

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

リストは別のオブジェクトを指しているため、リストビューのデータには影響しません。このオブジェクトはアダプターの新しいオブジェクトについて何も認識せず、notifyDataSetChanged()は何も影響しません。したがって、オブジェクトのデータを変更し、アダプター用の新しいオブジェクトを再度作成しないようにする必要があります


10
アダプタオブジェクトを再作成していますが、効率的ではありません
Alezis

2
@AlezisそれはまさにNhanが意味したことだと思います。
Neeraj Sewani、

17

この問題の背後にある理由と、別の回答スレッドそれを処理する方法については、ここで説明しました。ここでもソリューションの概要を共有しています。

主な理由の1つがnotifyDataSetChanged()機能しない-

アダプタはリストへの参照を失います

新しいリストを作成してに追加するときAdapter。常に次のガイドラインに従ってください。

  1. arrayListグローバルに宣言しながらwhileを初期化します。
  2. リストをアダプタに直接追加し、null値や空の値をチェックしません。アダプターをリストに直接設定します(条件を確認しないでください)。アダプターは、データに変更を加えた場合はどこでもarrayListそれが処理されることを保証しますが、参照を失うことはありません。
  3. 常に(あなたのデータはあなたが呼び出すことができるよりも、完全に新規の場合ArrayListを自身のデータを変更adapter.clear()してarrayList.clear()、リストにデータを追加実際に前)が、新しいデータがに移入されている場合は、アダプタすなわちを設定していないarrayList以上、単に adapter.notifyDataSetChanged()

お役に立てれば。


1
ありがとう!#2のヒントが役立ちました。
ケルビー2017

4

たぶんあなたのリストビューを更新してみてください:

receiptsListView.invalidate()

編集:別の考えが私の頭に浮かびました。念のため、リストビューのキャッシュを無効にしてみてください。

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

奇妙なことに、私の最後のアイデアは、データが変更された後にリストビューにアダプターを再割り当てすることです。しかし、あなたもすでにこれを試したと思います。
RafalGałka2013年

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。問題を解決するlistarraylist解決しない場合、for exの2つ以上のインスタンスを作成している可能性がありますadapter

主な活動

...

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

...


SomeFragment

...

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

...

2番目のケースで、リサイクラービューのインフレートされたビューのリストの変更が予想される場合adapter、リサイクラービューにアタッチされていないの新しいインスタンスが2回目に作成されるため、これは起こりません。notifyDataSetChanged2番目のアダプターの設定は、recycerビューの内容を変更するつもりはありません。そのためには、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

初期化する前に、アダプタとオブジェクトのリストがnullかどうかを確認する必要があります

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.