Die Android ListView Klasse wird zum Anzeigen von Listen verwendet. Im folgenden Beispiel wird eine ListView basierte Android App erstellt und auf die Grundlagen von Listenansichten eingegangen.

Android ListView Beispiel App:

Wir beginnen mit dem Erstellen einer neuen App. Geben Sie im Assistenten zum Erstellen eines neuen Android Projektes bitte folgende Daten ein:

Application Name: Listenbeispiel
Create Activity: Blank Activity with Fragment

Als nächstes werden die angezeigten Daten erstellt. Dazu in Eclipse im Ordner res/values eine neue XML Datei mit dem Namen produkte.xml anlegen und folgenden Inhalt hineinkopieren:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="produkte">
        <item name="schinkenspeck">Schinkenspeck</item>
        <item name="bauchspeck">Bauchspeck</item>
        <item name="karreespeck">Karreespeck</item>
        <item name="schopfsspeck">Schopfspeck</item>
    </string-array>    
</resources>

Im nächsten Schritt wird das PlaceholderFragment aus MainActivity.java um die Listenansicht erweitert. Dazu in MainActivity.java den Typ der Vaterklasse des PlaceholderFragment von Fragment auf ListFragment ändern. Im Anschluss daran muss ListFragment noch importiert werden – am einfachsten durch den Eclipse-Shortcut Ctrl+Shift+O

Anschließend im Layout fragment_main.xml das TextView Element entfernen und ein ListView Steuerelement (in Palette unter Composite) hinzufügen. Nun muss die ID des ListView Elements auf android:id=“@android:id/list“ geändert werden. Dies ist notwendig, da ListFragment (sowie ListActivity) im Layout automatisch nach einer ListView mit der id „@android:id/list“ suchen.

Um nun die Daten aus dem XML string-array an die ListView zu übergeben, wird die Methode setListAdapter() aus ListFragment wie im folgendem Beispiel in onCreateView() aufgerufen:

String[] values = getResources().getStringArray(R.array.produkte);
setListAdapter(new ArrayAdapter&lt;String&gt;(getActivity(),
               android.R.layout.simple_list_item_1,
               values));

Hier passiert folgendes:

  • String[] values = getResources().getStringArray(R.array.produkte);
    Liefert ein Array vom Typ String aus der XML Datei produkte.xml
  • new ArrayAdapter<String>
    Legt ein Objekt vom Typ ArrayAdapter mit Werten vom Typ String an
  • getActivity()
    Liefert den aktuellen Context
  • android.R.layout.simple_list_item_1
    Verwendet ein bestehendes Android Layout zur Darstellung der Listeneinträge
  • values
    Stellt die Daten dem ArrayAdapter bereit

Wird das soeben erstellte ListView Beispiel ausgeführt, bekommen wir folgendes zu sehen:

Android ListView Beispiel App

Auf Benutzereingaben der ListView reagieren

Um von der Berührung eines Listenelements benachrichtigt zu werden, muss lediglich die Methode onListItemClick() aus ListFragment überschrieben werden. Hierzu ein Beispiel:

@Override
public void onListItemClick(ListView l, View v, int position, long id) {
    String item = (String) getListAdapter().getItem(position);
    Toast.makeText(getActivity(), item + " ausgewählt",
             Toast.LENGTH_SHORT).show();
}

Eigene Layouts zur Anzeige in ListView

Im zweiten Teil der Übung wird die Anzeige der Listeneinträge von einem eigenem Layout übernommen, wodurch sich die ListView, bzw. deren Einträge beliebig gestalten lässt.

  1. Hierzu wird zuerst ein neues Layout erstellt:
    In Eclipse, Rechtsklick über dem Ordner Layout -> New -> Other -> Android XML File.
    Als Name listenelement.xml vergeben und ein Root Element vom Typ LinearLayout wählen.
  2. Fügen Sie nun dem Layout ein ImageView sowie ein TextView Element hinzu und setzen Sie die Eigenschaft android:orientation von LinearLayout auf den Wert horizontal sowie die Eigenschaft scaleType der ImageView auf den Wert centerInside. Dadurch wird sichergestellt, dass das darzustellende Bild voll sichtbar ist. Das Ergebnis sollte folgendem Beispiel ähnlich sein:
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:orientation="horizontal" >
     
        <ImageView
            android:id="@+id/imageView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:scaleType="centerInside"
            android:src="@drawable/ic_launcher" />
     
        <TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingLeft="10dp"
            android:text="TextView"
            android:textSize="30sp" />
     
    </LinearLayout>
    
  3. Als nächstes kopieren Sie bitte die Beispieldateien in den Ordner res/drawable-xxhdpi.
  4. Im folgendem Schritt wird eine eigene Adapter Klasse implementiert, um auf das Erzeugen der View, die zum Anzeigen der Daten verwendet wird, Einfluss nehmen zu können. Dazu in Eclipse über einen Rechtsklick auf den Projektnamen -> New -> Class eine neue Klasse mit dem Namen MyAdapter anlegen und anschließend den generierten Code durch folgenden ersetzen:
    package com.individuapp.listenbeispiel;
    
    import android.content.Context;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.ArrayAdapter;
    import android.widget.ImageView;
    import android.widget.TextView;
    
    public class MyAdapter extends ArrayAdapter&lt;String&gt; {
    	public MyAdapter(Context context, int resource, int textViewResourceId, String[] objects) {
    		super(context, resource, textViewResourceId, objects);
    	}
    
    	@Override
    	public View getView(int position, View convertView, ViewGroup parent) {
    		Context ctx = parent.getContext();
    		LayoutInflater inflator = (LayoutInflater) ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    		View v = inflator.inflate(R.layout.listenelement, parent, false);
    
    		String[] values = ctx.getResources().getStringArray(R.array.produkte);
    		ImageView img = (ImageView) v.findViewById(R.id.imageView1);
    		TextView text = (TextView) v.findViewById(R.id.textView1);
    
    		text.setText(values[position]);
    		if (values[position].equals("Schinkenspeck")) {
    			img.setImageResource(R.drawable.schinkenspeck);
    		} else if (values[position].equals("Bauchspeck")) {
    			img.setImageResource(R.drawable.bauchspeck);
    		} else if (values[position].equals("Karreespeck")) {
    			img.setImageResource(R.drawable.karreespeck);
    		} else if (values[position].equals("Schopfspeck")) {
    			img.setImageResource(R.drawable.schopfspeck);
    		}
    
    		return v;
    	}
    }
    

Hier passiert folgendes:

  • Die Klasse MyAdapter erweitert die Klasse ArrayAdapter (Zeile 11), die sich um die Aufbereitung der Daten und das Erstellen der Benutzerschnittstelle kümmert.
  • Die überschriebene Methode getView() erledigt das Erstellen der View (Ansicht) eines Listenelements. Hierbei wird zuerst über den LayoutInflater Dienst eine View aus der Datei listenelement.xml erstellt (Zeile 19 und 20).
  • In Zeile 22 werden die Werte aus der Datei produkte.xml gelesen.
  • In den Zeilen 23 und 24 werden die Referenzen auf das ImageView und TextView Element abgefragt.
  • Der TextView wird in Zeile 26 der darzustellende Text zugwiesen.
  • Der ImageView wird in den Zeilen 27 bis 35 abhängig vom Text ein Bild zugewiesen.

Damit das Erzeugen der Ansicht eines Listenelements von der Klasse MyAdapter erledigt wird, muss in MainActivity noch der Aufruf von setListAdapter() wie folgt abgeändert werden:

setListAdapter(new MyAdapter(getActivity(), 
               android.R.layout.simple_list_item_1, 
               R.id.textView1, values));

Wenn die ListView Beispiel App nun gestartet wird, sehen wir folgendes:

Android Beispiel mit eingen Layout in ListView.

Performanceoptimierung von ListView.

Wiederverwenden bestehender Views.

Eine ListView enthält in der Regel mehr Datensätze, als angezeigt werden können. Da das Erzeugen der View (Ansicht) von Listeneinträgen zur Laufzeit geschieht und sowohl sehr rechen- als auch speicherintensiv ist, empfiehlt es sich, so wenig Views wie möglich zu erzeugen.

Android erlaubt die Wiederverwendung der Views von Listeneinträgen, die aus dem sichtbaren Bereich der ListView hinausgescrollt wurden. Dies geschieht über den Parameter convertView der Methode getView(). Falls der Parameter convertView einen Wert ungleich null enthält, kann convertView neue Daten übergeben werden und das Erzeugen einer neuen View übersprungen werden.

Das View Holder Entwurfsmuster.

Über das View Holder Entwurfsmuster kann der Aufruf der Methode findViewById(), die zum Abfragen einer Referenz eines Steuerelements dient, vermieden werden. Hierzu wird eine statische ViewHolder Klasse innerhalb der Adapterklasse definiert, die die Referenzen der verschiedenen Steuerelemente speichert. Die ViewHolder Klasse wird der View des Listenelements durch die Methode setTag() übergeben.

Wird im Anschluss die Methode getView() mit einer wiederverwendbaren View im Parameter convertView aufgerufen, kann die ViewHolder Klasse über die Methode getTag() abgefragt werden.

Im folgenden Beispiel wurde MyAdapter um das Recyclen von Listeneinträgen sowie um die Verwendung des ViewHolder Entwurfsmusters erweitert:

public class MyAdapter extends ArrayAdapter&lt;String&gt; {
    Context  context;
    String[] values;
    
    public MyAdapter(Context context, int resource, int textViewResourceId, String[] objects) {
        super(context, resource, textViewResourceId, objects);
        
        this.context = context;
        this.values = context.getResources().getStringArray(R.array.produkte);
    }
    
    static class ViewHolder {
        public TextView text;
        public ImageView image;
    }
    
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View retVal = convertView;
        
        // Bestehende View wiederverwenden.
        if (null == retVal) {
            LayoutInflater inflator = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            retVal = inflator.inflate(R.layout.listenelement, parent, false);
            
            ViewHolder vHolder = new ViewHolder();
            vHolder.text = (TextView) retVal.findViewById(R.id.textView1);
            vHolder.image = (ImageView) retVal.findViewById(R.id.imageView1);

            retVal.setTag(vHolder);
        }
        
        // Daten übergeben
        ViewHolder vHolder = (ViewHolder) retVal.getTag();
        vHolder.text.setText(values[position]);
        if (values[position].equals("Schinkenspeck")) {
            vHolder.image.setImageResource(R.drawable.schinkenspeck);
        } else if (values[position].equals("Bauchspeck")) {
            vHolder.image.setImageResource(R.drawable.bauchspeck);
        } else if (values[position].equals("Karreespeck")) {
            vHolder.image.setImageResource(R.drawable.karreespeck);
        } else if (values[position].equals("Schopfspeck")) {
            vHolder.image.setImageResource(R.drawable.schopfspeck);
        }
            
        return retVal;
    }
}