informDataSetChange ไม่ทำงานจากอะแดปเตอร์ที่กำหนดเอง


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

}

--EDIT:

พบวิธีแก้ปัญหา

เพียงแค่มีรหัสการทำงานฉันทำตอนนี้:

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

ใช้งานได้ แต่ไม่ใช่วิธีการทำงาน


stackoverflow.com/a/4198569/2382964สวัสดี Jasper โปรดดูลิงค์นี้ ... สิ่งนี้จะช่วยคุณได้
Tushar Pandey

ลองใช้วิธีอื่น ๆ เช่น alertItemInserted, informItemRemoved ฯลฯ ..
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

@ prom85 ArrayAdapters สามารถผูกกับ Objects อื่นที่ไม่ใช่ Lists หรือ Arrays ได้หรือไม่? ฉันไม่รู้.
tolgap

มันเกี่ยวกับ a BaseAdapterและอะแดปเตอร์นี้ไม่ทราบว่าข้อมูลใดถูกผูกไว้ ... ดังนั้นหากฉันมีวัตถุที่กำหนดเองและใช้ฟังก์ชันที่กำหนดเองของวัตถุนี้ (เช่นcustObject.getCount()และcustObject.getChildAt(int i)ตัวอย่าง) และฉันต้องการแลกเปลี่ยนวัตถุนี้notifyDataSetChangedจะไม่ทำงาน ... อย่างไรก็ตามฉันคิดว่าปัญหานี้ไม่เคยเกิดขึ้นกับArrayAdapter
prom85

3
คุณช่วยอธิบายได้ไหมว่าทำไมวิธีแรกไม่ได้ผลและวิธีที่สองใช้กับ BaseAdapter ได้หรือไม่?
Tooroop

1
ไม่อนุญาตให้แสดงความคิดเห็นเช่นขอบคุณหรือ +1 แต่สำหรับบางคำตอบฉันอยากจะขอบคุณจริงๆ :)
Muhammad Saqib

24

ฉันมีปัญหาเดียวกันและฉันก็ตระหนักดีว่า เมื่อเราสร้างอะแดปเตอร์และตั้งค่าเป็น listview listview จะชี้ไปที่ออบเจ็กต์ที่ใดที่หนึ่งในหน่วยความจำที่อะแดปเตอร์เก็บไว้ข้อมูลในออบเจ็กต์นี้จะแสดงใน listview

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

ถ้าเราสร้างออบเจ็กต์สำหรับอะแดปเตอร์ด้วยข้อมูลอื่นอีกครั้งและแจ้งเตือนข้อมูลชุดเปลี่ยนแปลง ():

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

สิ่งนี้จะไม่มีผลกับข้อมูลใน listview เนื่องจากรายการชี้ไปที่วัตถุอื่นวัตถุนี้ไม่ทราบอะไรเกี่ยวกับวัตถุใหม่ในอะแดปเตอร์และ informDataSetChanged () ไม่มีผลกับอะไร ดังนั้นเราจึงควรเปลี่ยนข้อมูลในวัตถุและหลีกเลี่ยงการสร้างวัตถุใหม่อีกครั้งสำหรับอะแดปเตอร์


10
คุณกำลังสร้างอ็อบเจ็กต์อะแดปเตอร์ใหม่มันไม่มีประสิทธิภาพ
Alezis

2
@Alezis ฉันคิดว่านั่นคือสิ่งที่แนนหมายถึง
Neeraj Sewani

17

ดังที่ฉันได้อธิบายเหตุผลเบื้องหลังปัญหานี้ไปแล้วและวิธีจัดการในหัวข้อคำตอบอื่นที่นี่ที่นี่ฉันยังคงแบ่งปันสรุปการแก้ปัญหาที่นี่

สาเหตุหลักประการหนึ่งที่ใช้notifyDataSetChanged()ไม่ได้กับคุณ - คือ

อะแดปเตอร์ของคุณสูญเสียการอ้างอิงถึงรายการของคุณ

เมื่อสร้างและเพิ่มรายการใหม่ในไฟล์Adapter. ปฏิบัติตามคำแนะนำเหล่านี้เสมอ:

  1. เริ่มต้นในarrayListขณะที่ประกาศทั่วโลก
  2. เพิ่มรายการลงในอะแด็ปเตอร์โดยตรงโดยไม่ต้องตรวจสอบค่าว่างและค่าว่าง ตั้งค่าอะแดปเตอร์เป็นรายการโดยตรง (อย่าตรวจสอบเงื่อนไขใด ๆ ) อะแดปเตอร์รับประกันคุณว่าทุกที่ที่คุณทำการเปลี่ยนแปลงข้อมูลของarrayListมันจะดูแลมัน แต่จะไม่หลุดข้อมูลอ้างอิง
  3. แก้ไขข้อมูลใน arrayList เสมอ (หากข้อมูลของคุณใหม่ทั้งหมดเกินกว่าที่คุณจะเรียกได้adapter.clear()และarrayList.clear()ก่อนที่จะเพิ่มข้อมูลลงในรายการจริง) แต่อย่าตั้งค่าอะแด็ปเตอร์เช่นหากข้อมูลใหม่ถูกเติมในarrayListมากกว่า adapter.notifyDataSetChanged()

หวังว่านี่จะช่วยได้


1
ขอบคุณ! เคล็ดลับที่ # 2 ช่วยได้
Cheruby

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ตั้งค่าเป็น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


1

หากมีโอกาสที่คุณเข้ามาในกระทู้นี้และสงสัยว่าเหตุใดadapter.invaidate()หรือไม่มีadapter.clear()วิธีการใดในกรณีของคุณอาจเป็นเพราะคุณอาจใช้สิ่งที่ผู้ถามของคำถามนี้ใช้RecyclerView.Adapterแทน BaseAdapterหากการล้างlistหรือarraylistไม่สามารถแก้ไขปัญหาของคุณได้อาจเกิดขึ้นได้ว่าคุณกำลังสร้างตัวอย่างadapter for 2 ครั้งขึ้นไป:

กิจกรรมหลัก

...

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

...

และ
SomeFragment

...

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

...

หากในกรณีที่สองคุณคาดว่าจะมีการเปลี่ยนแปลงในรายการมุมมองที่สูงเกินจริงในมุมมองรีไซเคิลมันจะไม่เกิดขึ้นเหมือนในครั้งที่สองที่มีการสร้างอินสแตนซ์ใหม่adapterซึ่งไม่ได้เชื่อมต่อกับมุมมองผู้รีไซเคิล การตั้งค่าnotifyDataSetChangedในอะแด็ปเตอร์ตัวที่สองจะไม่เปลี่ยนเนื้อหาของมุมมองรีไซเซอร์ สำหรับสิ่งนั้นให้สร้างอินสแตนซ์ใหม่ของมุมมองรีไซเคิลในSomeFragmentและแนบเข้ากับอินสแตนซ์ใหม่ของอะแด็ปเตอร์

SomeFragment

...

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

...

แม้ว่าฉันไม่แนะนำให้สร้างหลายอินสแตนซ์ของอะแดปเตอร์เดียวกันและมุมมองรีไซเคิล



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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.