NotifyDataSetChanged sonrasında Android ListView yenilenmiyor


116

Listem Parça kodu

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

Listem Görünümü

<?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>

Ancak bu ListView. Uygulamayı yeniden başlattıktan sonra bile güncellenen öğeler gösterilmiyor. Benim ItemAdapteruzanırBaseAdapter

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

Biri yardım edebilir mi lütfen?


2
Veritabanı işleminin düzgün çalışıp çalışmadığını test ettiniz mi? Adaptör nasıl görünüyor? Ayrıca, adapterreferans için bir nesne oluşturuyorsanız, neden aşağıda bir satır boş olup olmadığını test ediyorsunuz?
Luksprog

Kod istisna atmıyor ve hata ayıklamayı kullanarak kontrol ettim. Tüm yöntemler hatasız yürütülmüştür. Evet bu aptalca bir hata.
Coder

Yanıtlar:


229

Senin bak onResumeyöntemin ItemFragment:

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

çağırmadan önce güncellediğiniz notifyDataSetChanged()şey, bağdaştırıcının alanı private List<Item> items;değil, parçanın aynı şekilde beyan edilmiş alanıdır. Bağdaştırıcı, bağdaştırıcıyı oluşturduğunuzda geçirdiğiniz öğelerin listesine hala bir referans saklar (örneğin, parçanın onCreate'inde). Kodunuzun beklediğiniz gibi davranmasını sağlamanın en kısa yolu (değişiklik sayısı anlamında) ancak şık olmayan yolu, satırı değiştirmektir:

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

ile

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

Daha zarif bir çözüm:

1) öğeleri private List<Item> items;buradan kaldırın ItemFragment- bunlara yalnızca adaptörde referans vermemiz gerekir

2) onCreate to:

@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'da yöntem ekleyin:

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

4) onResume'unuzu şu şekilde değiştirin:

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

Tüm dbHelper şeyini Adaptörün içine taşımak daha temiz olmaz mıydı? Yani sadece arayacaksın adapter.swapItems();ve adaptör işi yapacaktı dbHelper.getItems(). Yine de cevabınız için teşekkürler :)
Ansgar

7
Maddeleri neden temizleyip () tekrar eklemelisiniz? Bu tam olarak amacı değil notifyDataSetChanged()mi?
Phil Ryan


1
Teşekkürler @tomsaz Gawel, takas öğeleriniz gerçekten bana çok yardımcı oluyor, bağdaştırıcımın neden çalışmadığını bilmiyorum, geçtiğim "liste" de güncellendi, hatta günlüğü yazdırarak kontrol ettim, lütfen bana açıklar mısınız concept
Kimmi Dhingra

1
Bu cevap doğrudur. Sorun, ADAPTÖRÜN Öğe Dizisi Listesinin güncellenmemesidir. Bu, yüzünüz herhangi bir etki olmaksızın mavi olana kadar değiştirilmiş bildirim çağrısı yapabileceğiniz anlamına gelir. Bağdaştırıcı, veri kümenizi aynı veri kümesiyle güncelliyor, bu nedenle HİÇBİR değişiklik olmaz. Bu yanıtta yayınlanan çözümün daha temiz olabilecek bir başka alternatifi şudur: adapter.items = items; ) (adapter.notifyDataSetChanged;
Ray Li

23

İçindeki global değişken öğelere yeniden yüklenen öğeleri atıyorsunuz onResume(), ancak bu, ItemAdapter'öğeler' adı verilen kendi örnek değişkenine sahip olduğu için sınıfa yansıtılmayacaktır .

Canlandırıcı için ListView, bir yenileme () ekleyin ItemAdapterlistesi verileri yani öğeleri kabul sınıfın

class ItemAdapter
{
    .....

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

onResume()aşağıdaki kodla güncelle

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

1
Bu kesinlikle doğru. Bağdaştırıcının kurucusu öğelerin geçirilmesini bekler, ancak yalnızca dış sınıfın alanını günceller.
LuxuryMode

Merhaba Santhosh. Benzer bir soruna bakabilir misiniz: stackoverflow.com/questions/35850715/…

8

OnResume () 'de bu satırı değiştirin

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

için

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

Sorun, bağdaştırıcınıza yeni öğeler listesinden asla bahsetmemenizdir. Bağdaştırıcınıza yeni bir liste geçirmek istemiyorsanız (öyle görünmüyor gibi), o zaman sadeceitems.addAll senin after clear(). Bu, adaptörün referans aldığı aynı listeyi değiştirmenizi sağlayacaktır.


adapter.clear()Bağdaştırıcıyı görünümün yenilenmesi gerektiğini, ancak adapter.add()veya yenilemesi gerektiğini anlamaya zorlamayan kafa karıştırıcı adapter.addAll(). Yine de cevap için teşekkürler!
w3bshark

items.addAll()Adapter.addAll () kullanmadığımı ve kullandığımı unutmayın . Adaptörün değişikliklere tepki vermesine izin veren tek şey notifyDataSetChanged. Bağdaştırıcının tüm değişiklikleri görmesinin nedeni, itemslistenin bağdaştırıcının kullandığı listeyle aynı olmasıdır.
Justin Breitfeller

4

Adaptör zaten ayarlanmışsa, yeniden ayarlamak liste görünümünü yenilemeyecektir. Bunun yerine, önce listview'in bir bağdaştırıcıya sahip olup olmadığını kontrol edin ve ardından uygun yöntemi çağırın.

Liste görünümünü ayarlarken bağdaştırıcının yeni bir örneğini oluşturmanın pek iyi bir fikir olmadığını düşünüyorum. Bunun yerine bir nesne oluşturun.

BuildingAdapter adapter = new BuildingAdapter(context);

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

Ayrıca, UI Thread üzerinde yenileme listesini çalıştırmayı deneyebilirsiniz:

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

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

UI iş parçacığının nasıl uygulanacağından emin değilim. Ana etkinliğimde 3 parça (sekme) var ve sorudaki kod, liste görünümünü içeren parçalardan biriyle ilgili. Öğelerin geçişinin nedeni ItemAdapter, satırları renklendirmek istiyorum ve liste görünümü birden çok veri öğesi görüntülüyor. Adaptör kodunu gönderdim.
Coder

Listenizi dolduran kodunuzu "this" kullanarak örnek koduma koymanız gerekir. "etkinlik" yerine
AlexGo

NotifyDataSetChanged () öğesini farklı iş parçacığında çalıştırdığınızda bazı durumlarda güncellenmez, bu nedenle yukarıdaki çözüm bazı durumlar için doğrudur.
Ayman Al-Absi

4

Eğer listview üzerinde bunu yapmak istiyorsanız önemli değil güncellemek istiyorsanız onResume(), onCreate()ya da başka bir işlevde, sen sandığından zorunda ilk şey, adaptörün yeni bir örneğini oluşturmak gerekmez yani, sadece doldurmak tekrar verilerinizi içeren diziler. Fikir şuna benzer bir şey:

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

Bu cevap bağlı aynı kullanmalıdır Yani List<Item>Gözlerinde farklı Fragment. İlk adaptör başlatmanızda, listenizi öğelerle doldurursunuz ve adaptörü liste görünümünüze ayarlarsınız. Bundan sonra, öğelerinizdeki her değişiklikte, değerleri ana öğeden silmeniz ve ardından List<Item> itemsyeni öğeleriniz ve çağrınız ile yeniden doldurmanız gerekir notifySetDataChanged();.

İşte böyle çalışır : ).


Cevap için teşekkürler. Bahsettiğiniz gibi değişiklikleri yaptım. Kodumu gönderdim. Hala çalışmıyor. Artık yeni öğeler eklendiğinde liste görünümünü bile göstermiyor.
Coder

Kodu değiştirdim. Gözlemlenmesi gereken garip şey, öğenin DB'de güncellenmemesi
Coder


3

AlexGo'dan bir cevap benim için hile yaptı:

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

Liste Güncellemesi, güncelleme bir GUI olayından tetiklenmeden önce benim için çalıştı, dolayısıyla UI iş parçacığında oldu.

Ancak, listeyi başka bir olaydan / iş parçacığından güncellediğimde - yani uygulamanın dışından gelen bir çağrı, güncelleme UI iş parçacığında olmayacak ve getListView çağrısını yok saydı. Güncellemeyi runOnUiThread ile yukarıdaki gibi çağırmak benim için hile yaptı. Teşekkürler!!


3

Bunu dene

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

3
adpter.notifyDataSetInvalidated();

Bunu onPause()Activity sınıfı yönteminde deneyin .



1

Listeniz Adaptörün kendisinde bulunuyorsa, listeyi güncelleyen işlevi çağırmak da çağırmalıdır notifyDataSetChanged().

Bu işlevi UI Thread'den çalıştırmak benim için hile yaptı:

refresh()Adaptörü içindeki işlevi

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

Ardından, bu işlevi UI İş Parçacığından çalıştırın

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

Güncelleme ağ üzerinden farklı bir iş parçacığı üzerinden geldiği için bu gerçekten benim için bir fark yarattı.
Chuck

0

Şöyle dene:

this.notifyDataSetChanged();

onun yerine:

adapter.notifyDataSetChanged();

Sen zorunda notifyDataSetChanged()için ListViewadaptör sınıfına değil.


Tabii ki olmayacak, eğer etkinlik bir liste görünümüyle uzatılırsa tek şans
cmario
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.