ListView mit Adapter und eigener Datenstruktur

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • ListView mit Adapter und eigener Datenstruktur

    ListView ist für Anfänger nicht wirklich einfach - nach vielen Fragen im Forum hab ich nun mal ein Tutorial dazu gemacht. Ich gehe dabei auch auf einige andere Punkte ein, die bei Anfängern gerne mal Probleme bereiten.

    Zunächst mal die grobe Übersicht:
    ListView ist ein vertikal scrollendes Widget, das von einem Adapter die Listeneinträge aufbauen läßt und diese dann anzeigt. Dabei werden nur so viele Einträge erzeugt, bis der Bildschirm voll ist (und noch ein bisschen weiter).
    Wenn der Benutzer dann scrollt, werden vom ListView die benötigten Einträge vom Adapter nachgeladen (das habt ihr bestimmt schon bei einigen Apps bemerkt).

    Es gibt zwar einige spezialisierte Adapter (z.B. ArrayAdapter, CursorAdapter), aber ich nutze hier den BaseAdapter, da sieht man besser, was intern passiert.

    Die Daten verpacke ich in eine Klasse Datensatz, mehrere Datensätze kommen in eine ArrayList<Datensatz>. Diese ArrayList nutzt dann der BaseAdapter mit einem Layout und erzeugt die List-Einträge.

    Nun geht's los:

    Zunächst brauchen wir eine Datenstruktur für die Informationen, die in einem Listeneintrag eingebaut werden ("Datensatz").

    Java-Quellcode

    1. private class Datensatz {
    2. public String name; // besser setter und getter-Methoden schreiben, stört hier aber...
    3. public String datum; // die Umwandlung von Datum lasse ich weg - das ist ein anderes (großes) Problem
    4. public Datensatz(String name, String datum) {
    5. this.name = name;
    6. this.datum = datum;
    7. }
    8. }


    Die Datensätze kommen in eine ArrayList, diese benutzt dann der Adapter, um die einzelnen Views zu erzeugen. Die Variable datensaetze gilt für die ganze Activity:

    Java-Quellcode

    1. private ArrayList<Datensatz> datensaetze;
    2. private void initDatensaetze() {
    3. datensaetze = new ArrayList<Datensatz>();
    4. for (int i=0; i<nameArray.length; i++) {
    5. // hier aus Arrays auslesen, bei dir wahrscheinlich anders...
    6. Datensatz datensatz = new Datensatz(nameArray[i],datumArray[i]);
    7. datensaetze.add(datensatz);
    8. }
    9. }


    Damit ein Datensatz in der Liste als View-Element eingebaut werden kann, brauchst du ein Layout. Ich hab hier mal ein Beispiel mit ein paar nützlichen Formatierungen (speichern in Projekt/res/layout/mylistitemlayout.xml):

    HTML-Quellcode

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    3. android:layout_width="fill_parent"
    4. android:layout_height="wrap_content" >
    5. <LinearLayout
    6. android:id="@+id/Zeile1Container"
    7. android:layout_width="fill_parent"
    8. android:layout_height="wrap_content"
    9. android:orientation="horizontal" >
    10. <TextView
    11. android:id="@+id/Nummer"
    12. android:layout_width="100dp"
    13. android:layout_height="wrap_content"
    14. android:gravity="center_horizontal" />
    15. <TextView
    16. android:id="@+id/Datum"
    17. android:layout_width="wrap_content"
    18. android:layout_height="wrap_content"
    19. android:layout_weight="1"
    20. android:gravity="center_horizontal" />
    21. </LinearLayout>
    22. <LinearLayout
    23. android:id="@+id/Zeile2Container"
    24. android:layout_width="fill_parent"
    25. android:layout_height="wrap_content" >
    26. <TextView
    27. android:id="@+id/Name"
    28. android:layout_width="fill_parent"
    29. android:layout_height="wrap_content" />
    30. </LinearLayout>
    31. </LinearLayout>
    Alles anzeigen


    Nun kommt der Adapter, der dem ListView die einzelnen ListItems auf Anfrage liefert. Der Adapter muss auch ein getCount liefern, damit ListView weiß, wann Schluss ist. Außerdem braucht man meistens auch einen OnItemClickListener (dann dürfen die Items aber selber keine OnClickListener implementieren!). Meine Activity heißt übrigens MyListActivity - musst du wahrscheinlich anpassen.

    Java-Quellcode

    1. class MyItemAdapter extends BaseAdapter implements OnItemClickListener {
    2. private final LayoutInflater mInflater;
    3. public MyItemAdapter() {
    4. mInflater = (LayoutInflater) MyListActivity.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    5. }
    6. public int getCount() {
    7. return datensaetze.size();
    8. }
    9. public Datensatz getItem(int position) {
    10. return datensaetze.get(position);
    11. }
    12. public long getItemId(int position) {
    13. return (long) position;
    14. }
    15. public View getView(int position, View convertView, ViewGroup parent) {
    16. LinearLayout itemView = (LinearLayout) mInflater.inflate(R.layout.mylistitemlayout, parent, false);
    17. bindView(itemView, position);
    18. return itemView;
    19. }
    20. private void bindView(LinearLayout view, int position) {
    21. Datensatz datensatz = getItem(position);
    22. view.setId((int) getItemId(position));
    23. TextView nummerTextView = (TextView) view.findViewById(R.id.Nummer);
    24. TextView datumTextView = (TextView) view.findViewById(R.id.Datum);
    25. TextView nameTextView = (TextView) view.findViewById(R.id.Name);
    26. nummerTextView.setText(String.valueOf(position));
    27. datumTextView.setText(datensatz.datum);
    28. nameTextView.setText(datensatz.name);
    29. }
    30. public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    31. // Meldung ausgeben oder Intent bauen und Activity starten
    32. Datensatz gewaehlterDatensatz = datensaetze.get(position);
    33. }
    34. }
    Alles anzeigen


    Jetzt brauchen wir noch ein Layout für die MyListActivity:

    HTML-Quellcode

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    3. android:layout_width="fill_parent"
    4. android:layout_height="fill_parent" >
    5. <LinearLayout
    6. android:id="@+id/UeberschriftContainer"
    7. android:layout_width="fill_parent"
    8. android:layout_height="wrap_content"
    9. android:orientation="horizontal" >
    10. <TextView
    11. android:id="@+id/Ueberschrift"
    12. android:layout_width="wrap_content"
    13. android:layout_height="wrap_content"
    14. android:layout_weight="1"
    15. android:gravity="center_horizontal" />
    16. </LinearLayout>
    17. <ListView
    18. android:id="@+id/ListView1"
    19. android:layout_width="fill_parent"
    20. android:layout_height="wrap_content"
    21. android:layout_weight="1" />
    22. </LinearLayout>
    Alles anzeigen


    So, nun der krönende Abschluss in MyListActivity: Layout laden, Daten aufbauen, ListView mit dem Adapter verbinden:

    Java-Quellcode

    1. public class MyListActivity extends Activity {
    2. private MyItemAdapter myAdapter;
    3. @Override
    4. public void onCreate(Bundle savedInstanceState) {
    5. super.onCreate(savedInstanceState);
    6. setContentView(R.layout.mylistlayout);
    7. initDatensaetze();
    8. ListView l = (ListView) findViewById(R.id.ListView1);
    9. myAdapter = new MyItemAdapter();
    10. l.setAdapter(myAdapter);
    11. l.setOnItemClickListener(myAdapter);
    12. }
    13. @Override
    14. protected void onResume() {
    15. super.onResume();
    16. // falls wir vom Änderungsformular zurückkommen...
    17. myAdapter.notifyDataSetChanged();
    18. }
    19. // hier die Java-Quelltext-Schnipsel von weiter oben einfügen:
    20. // Datensatz
    21. // initDatensaetze
    22. // MyItemAdapter
    23. }
    Alles anzeigen


    Jetzt hab ich hoffentlich alles zusammen - leider muss man sich das aus verschiedenen Quellen zusammen sammeln und Anfänger haben da manchmal zu viele lose Enden in der Hand. Aber hiermit solltest du klar kommen - Erweiterungsmöglichkeiten sind vor allem in Datensatz und bindView sinnvoll, je nach Quelle der Daten ist initDatensaetze auch einfach anzupassen.

    Fehler und Verbesserungen sind willkommen. :D
    Das Leben kann so hart sein, man muss nur richtig wollen!!!

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von Kogoro () aus folgendem Grund: Bedankomat aktiviert

  • Absolut das was ich gesucht habe!

    Wow, vielen Dank!
    Ich habe den ARtikel jetzt zwar nur gelesen und noch nicht ausprobiert (sei glücklich und froh es hätte schlimmer kommen können und ....), also wenn nicht schief geht freue ich michdarauf das Beispiel als Grundgerüst zu verwenden und aufzubauen. Ich denke mal mit ImageViews z.B. kann man das auch noch erweitern.

    Großes danke an den Autor!

    Gruß
    Oli
  • ist ja schon etwas älter und ein angenehm übersichtliches Beispiel - aber: hat das schonmal jemand so zum Laufen gebracht? Ich bekomme nur ein weißes Fenster angezeigt.

    @UweApps: bist du noch aktiv? Würde mich gerne mit dir zu diesem Beispiel austauschen.

    Eine Sache konnte ich gerade lösen:
    Ich hatte eine Reihe "Log.d()" Befehle eingebaut und dabei ist mir aufgefallen, dass die Funktionen getView() und somit auch bindView() jeweils 27 Mal durchlaufen wurden - und das bei nur drei Elementen im nameArray bzw. datumArray... Die Ursache hierfür ist die Verwendung von "wrap_content" im Layout der ListView (xml-Datei). Ich muss zugeben, dass ich die Details, WARUM das so ist, noch nicht genau verstanden habe - aber - Verändern zu "match_parent" hilft.

    Die "Erleuchtung" zu diesem Thema fand ich hier:
    youtube.com/watch?v=wDBM6wVEO70

    speziell (etwa bei 40:45):
    youtube.com/watch?v=wDBM6wVEO70&t=2445

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von colibri () aus folgendem Grund: Teil-Lösung hinzugefügt