Android ListView ไม่รีเฟรชหลังจาก alertDataSetChanged


116

รหัส ListFragment ของฉัน

public class ItemFragment extends ListFragment {

    private DatabaseHandler dbHelper;
    private static final String TITLE = "Items";
    private static final String LOG_TAG = "debugger";
    private ItemAdapter adapter;
    private List<Item> items;


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.item_fragment_list, container, false);        
        return view;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.setHasOptionsMenu(true);
        super.onCreate(savedInstanceState);
        getActivity().setTitle(TITLE);
        dbHelper = new DatabaseHandler(getActivity());
        items = dbHelper.getItems(); 
        adapter = new ItemAdapter(getActivity().getApplicationContext(), items);
        this.setListAdapter(adapter);

    }



    @Override
    public void onResume() {
        super.onResume();
        items.clear();
        items = dbHelper.getItems(); //reload the items from database
        adapter.notifyDataSetChanged();
    }

    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        super.onListItemClick(l, v, position, id);
        if(dbHelper != null) { //item is edited
            Item item = (Item) this.getListAdapter().getItem(position);
            Intent intent = new Intent(getActivity(), AddItemActivity.class);
            intent.putExtra(IntentConstants.ITEM, item);
            startActivity(intent);
        }
    }
}

มุมมองรายการของฉัน

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <ListView
        android:id="@android:id/list"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

แต่สิ่งนี้ไม่ได้รีเฟรชไฟล์ListView. แม้ว่าจะรีสตาร์ทแอปแล้วรายการที่อัปเดตจะไม่แสดง ItemAdapterขยายของฉันBaseAdapter

public class ItemAdapter extends BaseAdapter{

    private LayoutInflater inflater;
    private List<Item> items;
    private Context context;

    public ProjectListItemAdapter(Context context, List<Item> items) {
        super();
        inflater = LayoutInflater.from(context);
        this.context = context;
        this.items = items;

    }

    @Override
    public int getCount() {
        return items.size();
    }

    @Override
    public Object getItem(int position) {
        return items.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ItemViewHolder holder = null;
        if(convertView == null) {
            holder = new ItemViewHolder();
            convertView = inflater.inflate(R.layout.list_item, parent,false);
            holder.itemName = (TextView) convertView.findViewById(R.id.topText);
            holder.itemLocation = (TextView) convertView.findViewById(R.id.bottomText);
            convertView.setTag(holder);
        } else {
            holder = (ItemViewHolder) convertView.getTag();
        }
        holder.itemName.setText("Name: " + items.get(position).getName());
        holder.itemLocation.setText("Location: " + items.get(position).getLocation());
        if(position % 2 == 0) {                                                                                 
            convertView.setBackgroundColor(context.getResources().getColor(R.color.evenRowColor));
        } else {    
            convertView.setBackgroundColor(context.getResources().getColor(R.color.oddRowColor));
        }
        return convertView;
    }

    private static class ItemViewHolder {
        TextView itemName;
        TextView itemLocation;
    }
}

ใครสามารถช่วยกรุณา?


2
คุณได้ทดสอบเพื่อดูว่าการทำงานของฐานข้อมูลทำงานอย่างถูกต้องหรือไม่? อะแดปเตอร์มีลักษณะอย่างไร? นอกจากนี้หากคุณสร้าง on object สำหรับการadapterอ้างอิงทำไมคุณถึงทดสอบ null หนึ่งบรรทัดด้านล่าง
Luksprog

รหัสไม่ส่งข้อยกเว้นและฉันตรวจสอบโดยใช้การดีบัก วิธีการทั้งหมดดำเนินการโดยไม่มีข้อผิดพลาด ใช่นั่นเป็นความผิดพลาดโง่ ๆ
Coder

คำตอบ:


229

ดูonResumeวิธีการของคุณในItemFragment:

@Override
public void onResume() {
    super.onResume();
    items.clear();
    items = dbHelper.getItems(); // reload the items from database
    adapter.notifyDataSetChanged();
}

สิ่งที่คุณเพิ่งอัปเดตก่อนที่จะเรียกnotifyDataSetChanged()ไม่ใช่ฟิลด์ของอะแด็ปเตอร์private List<Item> items;แต่เป็นฟิลด์ที่ประกาศเหมือนกันของแฟรกเมนต์ อะแด็ปเตอร์ยังคงจัดเก็บการอ้างอิงไปยังรายการไอเท็มที่คุณส่งผ่านเมื่อคุณสร้างอะแด็ปเตอร์ (เช่นใน onCreate ของแฟรกเมนต์) วิธีที่สั้นที่สุด (ในแง่ของจำนวนการเปลี่ยนแปลง) แต่ไม่ใช่วิธีที่สวยงามในการทำให้โค้ดของคุณทำงานตามที่คุณคาดหวังคือเพียงแค่แทนที่บรรทัด:

    items = dbHelper.getItems(); // reload the items from database

กับ

    items.addAll(dbHelper.getItems()); // reload the items from database

โซลูชันที่หรูหรายิ่งขึ้น:

1) ลบรายการprivate List<Item> items;ออกItemFragment- เราจำเป็นต้องอ้างอิงถึงเฉพาะในอะแดปเตอร์เท่านั้น

2) เปลี่ยน onCreate เป็น:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    super.setHasOptionsMenu(true);
    getActivity().setTitle(TITLE);
    dbHelper = new DatabaseHandler(getActivity());
    adapter = new ItemAdapter(getActivity(), dbHelper.getItems());
    setListAdapter(adapter);
}

3) เพิ่มวิธีการใน ItemAdapter:

public void swapItems(List<Item> items) {
    this.items = items;
    notifyDataSetChanged();
}

4) เปลี่ยน onResume ของคุณเป็น:

@Override
public void onResume() {
    super.onResume();
    adapter.swapItems(dbHelper.getItems());
}

การย้าย dbHelper ทั้งหมดไปไว้ใน Adapter จะดีกว่าไหม ดังนั้นคุณจะโทรเท่านั้นadapter.swapItems();และอะแดปเตอร์จะทำdbHelper.getItems()สิ่งต่างๆ แต่ยังไงก็ขอบคุณสำหรับคำตอบ :)
Ansgar

7
ทำไมคุณต้องล้าง () และเพิ่มรายการอีกครั้ง? ไม่ตรงจุดประสงค์ของnotifyDataSetChanged()?
Phil Ryan

1
@tomsaz คุณช่วยฉันด้วยstackoverflow.com/questions/28148618/…

1
ขอบคุณ @tomsaz Gawel รายการ swap ของคุณช่วยฉันได้มากฉันไม่รู้ว่าทำไม adapter.notify ชุดข้อมูลของฉันเปลี่ยนไม่ทำงานเนื่องจาก "รายการ" ที่ฉันกำลังส่งนั้นได้รับการอัปเดตแม้ว่าฉันจะตรวจสอบแล้วโดยการพิมพ์บันทึกคุณช่วยอธิบายให้ฉันฟังหน่อยได้ไหม แนวคิด
Kimmi Dhingra

1
คำตอบนี้ถูกต้อง ปัญหาคือว่ารายการ ArrayList ของ ADAPTER ไม่ได้รับการอัพเดต ซึ่งหมายความว่าคุณสามารถโทรแจ้งชุดข้อมูลที่เปลี่ยนแปลงได้จนกว่าใบหน้าของคุณจะเป็นสีน้ำเงินโดยไม่มีผลใด ๆ อะแดปเตอร์กำลังอัปเดตชุดข้อมูลของคุณด้วยชุดข้อมูลเดียวกันดังนั้นจึงไม่มีการเปลี่ยนแปลง อีกทางเลือกหนึ่งสำหรับโซลูชันที่โพสต์ไว้ในคำตอบนี้ซึ่งอาจจะสะอาดกว่าคือ adapter.items = items adapter.notifyDataSetChanged ();
Ray Li

23

คุณกำลังกำหนดรายการที่โหลดซ้ำให้กับรายการตัวแปรส่วนกลางในonResume()แต่จะไม่สะท้อนในItemAdapterชั้นเรียนเนื่องจากมีตัวแปรอินสแตนซ์ของตัวเองที่เรียกว่า 'รายการ'

สำหรับการรีเฟรชListViewให้เพิ่มการรีเฟรช () ในItemAdapterคลาสซึ่งรับข้อมูลรายการเช่นรายการ

class ItemAdapter
{
    .....

    public void refresh(List<Item> items)
    {
        this.items = items;
        notifyDataSetChanged();
    } 
}

อัปเดตonResume()ด้วยรหัสต่อไปนี้

@Override
public void onResume()
{
    super.onResume();
    items.clear();
    items = dbHelper.getItems(); //reload the items from database
    **adapter.refresh(items);**
}

1
ตรงนี้แหละครับ คอนสตรัคเตอร์ของอะแด็ปเตอร์คาดว่าจะถูกส่งผ่านไอเท็ม แต่เขาจะอัปเดตฟิลด์ของคลาสภายนอกเท่านั้น
LuxuryMode

สวัสดี Santhosh ลองดูปัญหาที่คล้ายกันได้

8

ใน onResume () เปลี่ยนบรรทัดนี้

items = dbHelper.getItems(); //reload the items from database

ถึง

items.addAll(dbHelper.getItems()); //reload the items from database

ปัญหาคือคุณไม่เคยบอกอะแดปเตอร์ของคุณเกี่ยวกับรายการไอเท็มใหม่ หากคุณไม่ต้องการส่งรายการใหม่ไปยังอะแดปเตอร์ของคุณ (ดูเหมือนว่าคุณจะไม่ทำ) ให้ใช้items.addAllหลังจากclear()ไฟล์. เพื่อให้แน่ใจว่าคุณกำลังแก้ไขรายการเดียวกับที่อแด็ปเตอร์อ้างอิง


มันสับสนที่adapter.clear()ไม่ได้บังคับให้อะแด็ปเตอร์ตระหนักว่ามุมมองควรรีเฟรช แต่adapter.add()หรือadapter.addAll()ไม่ ขอบคุณสำหรับคำตอบ!
w3bshark

โปรดทราบว่าฉันใช้items.addAll()และไม่ใช่ adapter.addAll () สิ่งเดียวที่ทำให้อะแดปเตอร์ตอบสนองต่อการเปลี่ยนแปลงคือไฟล์notifyDataSetChanged. สาเหตุที่อะแด็ปเตอร์เห็นการเปลี่ยนแปลงเลยคือitemsรายการเป็นรายการเดียวกับที่อะแด็ปเตอร์ใช้
Justin Breitfeller

4

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

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

BuildingAdapter adapter = new BuildingAdapter(context);

    if(getListView().getAdapter() == null){ //Adapter not set yet.
     setListAdapter(adapter);
    }
    else{ //Already has an adapter
    adapter.notifyDataSetChanged();
    }

นอกจากนี้คุณอาจลองเรียกใช้รายการรีเฟรชบนเธรด UI:

activity.runOnUiThread(new Runnable() {         
        public void run() {
              //do your modifications here

              // for example    
              adapter.add(new Object());
              adapter.notifyDataSetChanged()  
        }
});

ฉันไม่แน่ใจว่าจะใช้เธรด UI อย่างไร กิจกรรมหลักของฉันมี 3 แฟรกเมนต์ (แท็บ) และโค้ดในคำถามเกี่ยวข้องกับแฟรกเมนต์หนึ่งที่มีมุมมองรายการ เหตุผลในการส่งรายการไปItemAdapterคือฉันต้องการสีของแถวและมุมมองรายการแสดงรายการข้อมูลหลายรายการ ฉันโพสต์รหัสสำหรับอะแดปเตอร์แล้ว
Coder

คุณต้องใส่รหัสของคุณที่เติมรายการของคุณในโค้ดตัวอย่างของฉันโดยใช้ "this" แทน "กิจกรรม"
AlexGo

ในบางกรณีจะไม่มีการอัปเดตเมื่อคุณเรียกใช้ informDataSetChanged () ในเธรดที่แตกต่างกันดังนั้นโซลูชันข้างต้นจึงเหมาะสมสำหรับบางกรณี
Ayman Al-Absi

4

หากคุณต้องการที่จะปรับปรุง ListView ของคุณไม่ว่าคุณจะต้องการที่จะทำว่าในวันที่onResume(), onCreate()หรือฟังก์ชั่นอื่น ๆ บางสิ่งแรกที่คุณต้องตระหนักคือการที่คุณจะไม่จำเป็นต้องสร้างตัวอย่างใหม่ของอะแดปเตอร์เพียงแค่เติม อาร์เรย์กับข้อมูลของคุณอีกครั้ง แนวคิดนี้คล้ายกับสิ่งนี้:

private ArrayList<String> titles;
private MyListAdapter adapter;
private ListView myListView;

@Override
public void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main_activity);

    myListView = (ListView) findViewById(R.id.my_list);

    titles = new ArrayList<String>()

    for(int i =0; i<20;i++){
        titles.add("Title "+i);
    }

    adapter = new MyListAdapter(this, titles);
    myListView.setAdapter(adapter);
}


@Override
public void onResume(){
    super.onResume();
    // first clear the items and populate the new items
    titles.clear();
    for(int i =0; i<20;i++){
        titles.add("New Title "+i);
    }
    adapter.notifySetDataChanged();
}

ดังนั้นขึ้นอยู่กับคำตอบนั้นคุณควรใช้คำตอบเดียวกันList<Item>ในFragment. ในการเริ่มต้นอะแด็ปเตอร์ครั้งแรกของคุณคุณต้องกรอกรายการของคุณด้วยรายการและตั้งค่าอะแดปเตอร์เป็น listview ของคุณ หลังจากนั้นในการเปลี่ยนแปลงในทุกรายการของคุณคุณต้องล้างค่าจากหลักและกว่าเติมอีกครั้งกับรายการใหม่ของคุณและโทรList<Item> itemsnotifySetDataChanged();

นั่นคือวิธีการทำงาน :)


ขอบคุณสำหรับการตอบกลับ. ฉันทำการเปลี่ยนแปลงตามที่คุณพูดถึง ฉันโพสต์รหัสของฉันแล้ว มันยังไม่ทำงาน ตอนนี้ไม่ได้แสดงมุมมองรายการเมื่อมีการเพิ่มรายการใหม่
Coder

ฉันเปลี่ยนรหัสแล้ว สิ่งที่น่าสังเกตคือรายการไม่ได้รับการอัปเดตใน DB
Coder

เธรดนี้ใช้สำหรับฐานข้อมูลstackoverflow.com/questions/14555332/…
Coder

3

คำตอบจาก AlexGo เป็นเคล็ดลับสำหรับฉัน:

getActivity().runOnUiThread(new Runnable() {
        @Override
        public void run() {
         messages.add(m);
         adapter.notifyDataSetChanged();
         getListView().setSelection(messages.size()-1);
        }
});

การอัปเดตรายการใช้งานได้สำหรับฉันก่อนหน้านี้เมื่อการอัปเดตถูกทริกเกอร์จากเหตุการณ์ GUI ดังนั้นจึงอยู่ในเธรด UI

อย่างไรก็ตามเมื่อฉันอัปเดตรายการจากเหตุการณ์ / เธรดอื่นเช่นการโทรจากภายนอกแอปการอัปเดตจะไม่อยู่ในเธรด UI และไม่สนใจการเรียกใช้ getListView การเรียกการอัปเดตด้วย runOnUiThread ดังที่กล่าวมาเป็นเคล็ดลับสำหรับฉัน ขอบคุณ !!


3

ลองทำตามนี้

@Override
public void onResume() {
super.onResume();
items.clear();
items = dbHelper.getItems(); //reload the items from database
adapter = new ItemAdapter(getActivity(), items);//reload the items from database
adapter.notifyDataSetChanged();
}



1

หากรายการของคุณมีอยู่ในตัวอะแดปเตอร์การเรียกใช้ฟังก์ชันที่อัปเดตรายการก็ควรเรียกใช้notifyDataSetChanged()เช่นกัน

การเรียกใช้ฟังก์ชันนี้จากเธรด UI ทำให้ฉันได้เคล็ดลับ:

refresh()ฟังก์ชั่นภายในอะแดปเตอร์

public void refresh(){
    //manipulate list
    notifyDataSetChanged();
}

จากนั้นเรียกใช้ฟังก์ชันนี้จากเธรด UI

getActivity().runOnUiThread(new Runnable() { 
    @Override
    public void run() {
          adapter.refresh()  
    }
});

สิ่งนี้สร้างความแตกต่างให้กับฉันอย่างแท้จริงเนื่องจากการอัปเดตผ่านเครือข่ายผ่านเธรดอื่น
Chuck

0

ลองทำดังนี้

this.notifyDataSetChanged();

แทน:

adapter.notifyDataSetChanged();

คุณต้องnotifyDataSetChanged()ไปที่ListViewไม่ได้ไปเรียนอะแดปเตอร์


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