wake-up-neo.com

Android ListView wird nach notifyDataSetChanged nicht aktualisiert

Mein ListFragment-Code 

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

Meine ListView

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

Die ListView wird dadurch jedoch nicht aktualisiert. Auch nach dem Neustart der App werden die aktualisierten Elemente nicht angezeigt. Meine ItemAdapter erweitert 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;
    }
}

Kann jemand bitte helfen?

106
Coder

Sehen Sie sich Ihre onResume-Methode in ItemFragment an:

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

was Sie vor dem Aufruf von notifyDataSetChanged() gerade aktualisiert haben, ist nicht das Feld private List<Item> items; des Adapters, sondern das identisch deklarierte Feld des Fragments. Der Adapter speichert immer noch einen Verweis auf die Liste der Elemente, die Sie beim Erstellen des Adapters übergeben haben (z. B. onCreate von fragment) . Die kürzeste (im Sinne der Anzahl der Änderungen), aber nicht die eleganteste Art und Weise, dass sich Ihr Code wie erwartet verhält einfach um die Zeile zu ersetzen:

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

mit 

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

Eine elegantere Lösung:

1) Entfernen Sie die Elemente private List<Item> items; von ItemFragment - wir müssen nur im Adapter darauf verweisen

2) Ändern Sie onCreate in:

@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) Methode in ItemAdapter hinzufügen:

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

4) Ändern Sie Ihren onResume in:

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

Sie weisen den globalen Variablenelementen in onResume() neu geladene Elemente zu. Dies gilt jedoch nicht für die Klasse ItemAdapter, da sie über eine eigene Instanzvariable mit dem Namen 'items' verfügt.

Fügen Sie für die Aktualisierung von ListView ein refresh () in der Klasse ItemAdapter hinzu, die Listendaten, d. H. Elemente, akzeptiert

class ItemAdapter
{
    .....

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

onResume() mit folgendem Code aktualisieren

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

In onResume () ändern Sie diese Zeile

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

zu

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

Das Problem ist, dass Sie Ihrem Adapter niemals von der Liste der neuen Artikel erzählen. Wenn Sie keine neue Liste an Ihren Adapter übergeben möchten (wie es scheint, tun Sie das nicht), verwenden Sie einfach items.addAll nach Ihrer clear(). Dadurch wird sichergestellt, dass Sie dieselbe Liste ändern, auf die der Adapter einen Verweis hat. 

8

Wenn Sie Ihre Listenansicht aktualisieren möchten, ist es unerheblich, ob Sie dies für onResume(), onCreate() oder eine andere Funktion tun möchten. Als erstes müssen Sie feststellen, dass Sie keine neue Instanz des Adapters erstellen müssen. Füllen Sie einfach die Arrays erneut mit Ihren Daten auf .. Die Idee sieht etwa so aus: 

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

Abhängig von dieser Antwort sollten Sie also dasselbe List<Item> in Ihrer Fragment verwenden. In Ihrer ersten Adapterinitialisierung füllen Sie Ihre Liste mit den Elementen und setzen den Adapter auf Ihre Listenansicht. Danach müssen Sie bei jeder Änderung Ihrer Elemente die Werte aus dem Hauptcode List<Item> items löschen und diese dann erneut mit Ihren neuen Elementen füllen und notifySetDataChanged(); aufrufen.

So funktioniert das : ).

4
hardartcore

Wenn der Adapter bereits festgelegt ist, wird die Listenansicht nicht aktualisiert, wenn Sie ihn erneut einstellen. Überprüfen Sie stattdessen zunächst, ob die Listenansicht über einen Adapter verfügt, und rufen Sie dann die entsprechende Methode auf.

Ich denke, es ist keine sehr gute Idee, eine neue Instanz des Adapters zu erstellen, während Sie die Listenansicht festlegen. Erstellen Sie stattdessen ein Objekt.

BuildingAdapter adapter = new BuildingAdapter(context);

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

Sie können auch versuchen, die Aktualisierungsliste im UI-Thread auszuführen:

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

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

Versuche dies 

@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
Gautami

Eine Antwort von AlexGo hat den Trick für mich gemacht:

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

List Update funktionierte früher für mich, als das Update durch ein GUI-Ereignis ausgelöst wurde und somit im UI-Thread lag.

Wenn ich jedoch die Liste von einem anderen Ereignis/Thread aus aktualisiere - d. H. Einem Aufruf von außerhalb der App, würde sich das Update nicht im UI-Thread befinden und der Aufruf von getListView wurde ignoriert. Das Update mit runOnUiThread wie oben beschrieben aufzurufen, war für mich der Trick. Vielen Dank!!

3
user2996950
adpter.notifyDataSetInvalidated();

Versuchen Sie dies in der onPause()-Methode der Aktivitätsklasse.

2
Som
adapter.setNotifyDataChanged()

sollte den Trick tun.

1
Hitman

Versuchen Sie es so: 

this.notifyDataSetChanged();

anstatt: 

adapter.notifyDataSetChanged();

Sie müssen notifyDataSetChanged() auf die ListView nicht auf die Adapterklasse.

0
Jachu

Wenn Ihre Liste im Adapter selbst enthalten ist, sollte beim Aufruf der Funktion, die die Liste aktualisiert, auch notifyDataSetChanged() aufgerufen werden.

Das Ausführen dieser Funktion aus dem UI-Thread hat für mich den Trick:

Die Funktion refresh() im Adapter

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

Führen Sie dann diese Funktion im UI-Thread aus

getActivity().runOnUiThread(new Runnable() { 
    @Override
    public void run() {
          adapter.refresh()  
    }
});
0
Dévan Coetzee