Lars Vogel
Version 4.6
Copyright © 2010, 2011, 2012, 2013, 2014 vogella GmbH
20.11.2014
Using Android ListView, ListActivity and ListFragment
This tutorial describes how to use the ListView view together with Activities and Fragments in Android. The tutorial is based on Eclipse 4.4, Java 1.7 and Android 5.0.
1. Android and Lists
The display of elements in a list is a very common pattern in mobile applications. The user sees a list of items and can scroll through them. Such an activity is depicted in the following picture.

Typically the user interacts with the list via the action bar, for example, via a refresh button. Individual list items can be selected. This selection can update the action bar or can trigger a detailed screen for the selection. The following graphic sketches that. On the selection of a list item another activity is started.

Android provides the ListView
and the ExpandableListView
classes which is capable of displaying a scrollable list of items.
The ExpandableListView
class supports a grouping of items.
The input to the list (items in the list) can be arbitrary Java objects. The adapter extracts the correct data from the data object and assigns this data to the views in the row of the ListView
.
These items are typically called the data model of the list. An adapter can receive data as input.
An adapter manages the data model and adapts it to the individual entries in the widget. An adapter extends the BaseAdapter
class.
Every line in the widget displaying the data consists of a layout which can be as complex as you want. A typical line in a list has an image on the left side and two text lines in the middle as depicted in the following graphic.

A layout file for a such a line might look like the following.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="?android:attr/listPreferredItemHeight" android:padding="6dip" > <ImageView android:id="@+id/icon" android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_alignParentBottom="true" android:layout_alignParentTop="true" android:layout_marginRight="6dip" android:contentDescription="TODO" android:src="@drawable/ic_launcher" /> <TextView android:id="@+id/secondLine" android:layout_width="fill_parent" android:layout_height="26dip" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:layout_toRightOf="@id/icon" android:ellipsize="marquee" android:singleLine="true" android:text="Description" android:textSize="12sp" /> <TextView android:id="@+id/firstLine" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_above="@id/secondLine" android:layout_alignParentRight="true" android:layout_alignParentTop="true" android:layout_alignWithParentIfMissing="true" android:layout_toRightOf="@id/icon" android:gravity="center_vertical" android:text="Example application" android:textSize="16sp" /> </RelativeLayout>
The adapter would inflate the layout for each row in its getView()
method and assign the data to the individual views in the row.
The adapter is assigned to the ListView
via the setAdapter
method on the ListView
object.
Tip
Adapters are not only used by ListView
, but also by other views which extend AdapterView
as, for example, Spinner
, GridView
, Gallery
and StackView
.
Filtering and sorting of the data is handled by the adapter. You need to implement the logic in your custom adapter implementation.
The notifyDataSetChanged()
method on the adapter is called if the data has changed or if new data is available.
The notifyDataSetInvalidated()
method is called if the data is not available anymore.
To react to selections in the list, set an OnItemClickListener
to your ListView
.
listView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Toast.makeText(getApplicationContext(), "Click ListItem Number " + position, Toast.LENGTH_LONG) .show(); } });
Android provides default adapter implementations; the most important are ArrayAdapter
and CursorAdapter
.
ArrayAdapter
can handle data based on Arrays
or java.util.List
.
SimpleCursorAdapter
can handle database related data.
The ArrayAdapter
class can handle a list or array of Java objects as input. Every Java object is mapped to one row. By default, it maps the toString()
method of the object to a view in the row layout.
You can define the ID of the view in the constructor of the ArrayAdapter
otherwise the android.R.id.text1
ID is used as default.
The ArrayAdapter
class allows to remove all elements in its underlying data structure with the clear()
method call. You can then add new elements via the add()
method or a Collection
via the addAll()
method.
You can also directly modify the underlying data structure and call the notifyDataSetChanged()
method on the adapter to notify it about the changes in data.
Warning
If you want to change the data in your adapter, the underlying data structure must support this operation. This is, for example, the case for the ArrayList
class, but not for arrays.
The following listing shows a layout file called activity_listviewexampleactivity.xml
which includes a ListView
.
<ListView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/listview" android:layout_width="wrap_content" android:layout_height="wrap_content" />
The following example shows the usage of the ListView
view in an activity. It uses a default layout from the Android platform for the row layout. It also demonstrates the removal of list items and uses animations for the removal.
package com.vogella.android.listview.withanimation; public class ListViewExampleActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_listviewexampleactivity); final ListView listview = (ListView) findViewById(R.id.listview); String[] values = new String[] { "Android", "iPhone", "WindowsMobile", "Blackberry", "WebOS", "Ubuntu", "Windows7", "Max OS X", "Linux", "OS/2", "Ubuntu", "Windows7", "Max OS X", "Linux", "OS/2", "Ubuntu", "Windows7", "Max OS X", "Linux", "OS/2", "Android", "iPhone", "WindowsMobile" }; final ArrayList<String> list = new ArrayList<String>(); for (int i = 0; i < values.length; ++i) { list.add(values[i]); } final StableArrayAdapter adapter = new StableArrayAdapter(this, android.R.layout.simple_list_item_1, list); listview.setAdapter(adapter); listview.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, final View view, int position, long id) { final String item = (String) parent.getItemAtPosition(position); view.animate().setDuration(2000).alpha(0) .withEndAction(new Runnable() { @Override public void run() { list.remove(item); adapter.notifyDataSetChanged(); view.setAlpha(1); } }); } }); } private class StableArrayAdapter extends ArrayAdapter<String> { HashMap<String, Integer> mIdMap = new HashMap<String, Integer>(); public StableArrayAdapter(Context context, int textViewResourceId, List<String> objects) { super(context, textViewResourceId, objects); for (int i = 0; i < objects.size(); ++i) { mIdMap.put(objects.get(i), i); } } @Override public long getItemId(int position) { String item = getItem(position); return mIdMap.get(item); } @Override public boolean hasStableIds() { return true; } } }
The ArrayAdapter
is limited as it supports only the mapping of toString()
to one view in the row layout. To control the data assignment and to support several views, you have to create your custom adapter implementation.
For this you would extend an existing adapter implementation or subclass the BaseAdapter
class directly.
Tip
Frequently you extend ArrayAdapter
to write a custom adapter, as this is simpler than extending BaseAdapter
directly.
The adapter needs to create a layout for each row of the list. The ListView
instance calls the getView()
method on the adapter for each data element. In this method the adapter creates the row layout and maps the data to the views in the layout.
This root of the layout is typically a ViewGroup
(layout manager) and contains several other views , e.g., an ImageView
and a TextView
. The following graphic shows a list with different layouts for odd and even rows.

Within the getView()
method you would inflate an XML based layout and then set the content of the individual views based on the Java object for this row. To inflate the XML layout file, you can use the LayoutInflator
system service.
Note
This layout inflator service can get accessed via the getLayoutInflator()
method of the activity or via the context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)
method call.
After the adapter inflated the layout, it searches for the relevant views in the layout and fills them with the data. The individual elements in the layout can be found via the findViewById()
method call on the top level view.
The following code shows an implementation of a custom adapter. This adapter assumes that you have two png files (no.png and yes.png) in one of your res/drawable
folders. The coding inflates an XML layout file, finds the relevant views in the layout and sets their content based on the input data.
package de.vogella.android.listactivity; 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 MySimpleArrayAdapter extends ArrayAdapter<String> { private final Context context; private final String[] values; public MySimpleArrayAdapter(Context context, String[] values) { super(context, R.layout.rowlayout, values); this.context = context; this.values = values; } @Override public View getView(int position, View convertView, ViewGroup parent) { LayoutInflater inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); View rowView = inflater.inflate(R.layout.rowlayout, parent, false); TextView textView = (TextView) rowView.findViewById(R.id.label); ImageView imageView = (ImageView) rowView.findViewById(R.id.icon); textView.setText(values[position]); // change the icon for Windows and iPhone String s = values[position]; if (s.startsWith("iPhone")) { imageView.setImageResource(R.drawable.no); } else { imageView.setImageResource(R.drawable.ok); } return rowView; } }
Android provides specialized fragment and activity classes to simplify list handling.
The classes are the ListActivity
class if you want to use lists in activities and the the ListFragment
class if you want to use lists in fragments.
You do not have to assign a layout to these elements. If you do not define a layout, the activity or fragment contains a single ListView
by default. ListActivity
and ListFragment
also allow you to override a onListItemClick()
method for handling selection of list items.
Both classes allow you to set the adapter to the default ListView
via the setListAdapter()
method.
The following example code shows a simple ListFragment
implementation.
package de.vogella.android.fragments; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.ArrayAdapter; import android.widget.ListView; import android.app.ListFragment; public class MyListFragment extends ListFragment { @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); String[] values = new String[] { "Android", "iPhone", "WindowsMobile", "Blackberry", "WebOS", "Ubuntu", "Windows7", "Max OS X", "Linux", "OS/2" }; ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, values); setListAdapter(adapter); } @Override public void onListItemClick(ListView l, View v, int position, long id) { // do something with the data } }
The next example code demonstrates the usage of a ListActivity
.
package de.vogella.android.listactivity; import android.app.ListActivity; import android.os.Bundle; import android.widget.ArrayAdapter; public class MyListActivity extends ListActivity { public void onCreate(Bundle icicle) { super.onCreate(icicle); String[] values = new String[] { "Android", "iPhone", "WindowsMobile", "Blackberry", "WebOS", "Ubuntu", "Windows7", "Max OS X", "Linux", "OS/2" }; ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, values); setListAdapter(adapter); } }
You can use a custom layout with ListActivity
or ListFragment
. In this case the fragment or activity searches in the provided layout for a ListView
with the pre-defined android:id
attribute set to @android:id/list
. This usage is demonstrated by the following code snippet.
<ListView android:id="@android:id/list" android:layout_width="match_parent" android:layout_height="wrap_content" > </ListView>
Warning
If you do not use this ID or do not include a ListView
into your layout, the application crashes once you try to display the activity or the fragment.
The following exercise demonstrates how to use a ListView
in an ListActivity
. You use the predefined ArrayAdapter
class and an existing Android layout for the rows.
Create a new Android project called de.vogella.android.listactivity with the activity called MyListActivity
.
Change MyListActivity
class based on the the following code example. Note that the setContentView()
method is not used.
package de.vogella.android.listactivity; import android.app.ListActivity; import android.os.Bundle; import android.view.View; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.Toast; public class MyListActivity extends ListActivity { public void onCreate(Bundle icicle) { super.onCreate(icicle); String[] values = new String[] { "Android", "iPhone", "WindowsMobile", "Blackberry", "WebOS", "Ubuntu", "Windows7", "Max OS X", "Linux", "OS/2" }; ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, values); setListAdapter(adapter); } @Override protected void onListItemClick(ListView l, View v, int position, long id) { String item = (String) getListAdapter().getItem(position); Toast.makeText(this, item + " selected", Toast.LENGTH_LONG).show(); } }

In our example your will define your layout for the rows and use it in your adapter.
Create the rowlayout.xml
layout file in the res/layout
folder of the de.vogella.android.listactivity
project.
<?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" > <ImageView android:id="@+id/icon" android:layout_width="22px" android:layout_height="22px" android:layout_marginLeft="4px" android:layout_marginRight="10px" android:layout_marginTop="4px" android:src="@drawable/ic_launcher" > </ImageView> <TextView android:id="@+id/label" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@+id/label" android:textSize="20px" > </TextView> </LinearLayout>
Change your activity so that is using the new layout.
package de.vogella.android.listactivity; import android.app.ListActivity; import android.os.Bundle; import android.view.View; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.Toast; public class MyListActivity extends ListActivity { public void onCreate(Bundle icicle) { super.onCreate(icicle); String[] values = new String[] { "Android", "iPhone", "WindowsMobile", "Blackberry", "WebOS", "Ubuntu", "Windows7", "Max OS X", "Linux", "OS/2" }; // use your custom layout ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.rowlayout, R.id.label, values); setListAdapter(adapter); } @Override protected void onListItemClick(ListView l, View v, int position, long id) { String item = (String) getListAdapter().getItem(position); Toast.makeText(this, item + " selected", Toast.LENGTH_LONG).show(); } }

The following uses two images “no.png” and “ok.png”. I placed it in the “res/drawable-mdpi” folder. You must create your own icons. In case you do not find any icons just copy “icon.png” and use a drawing program to change it a little bit.
Create the class MySimpleArrayAdapter
which will serve as our adapter.
package de.vogella.android.listactivity; 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 MySimpleArrayAdapter extends ArrayAdapter<String> { private final Context context; private final String[] values; public MySimpleArrayAdapter(Context context, String[] values) { super(context, R.layout.rowlayout, values); this.context = context; this.values = values; } @Override public View getView(int position, View convertView, ViewGroup parent) { LayoutInflater inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); View rowView = inflater.inflate(R.layout.rowlayout, parent, false); TextView textView = (TextView) rowView.findViewById(R.id.label); ImageView imageView = (ImageView) rowView.findViewById(R.id.icon); textView.setText(values[position]); // Change the icon for Windows and iPhone String s = values[position]; if (s.startsWith("Windows7") || s.startsWith("iPhone") || s.startsWith("Solaris")) { imageView.setImageResource(R.drawable.no); } else { imageView.setImageResource(R.drawable.ok); } return rowView; } }
To use this adapter, change the activity to the following.
package de.vogella.android.listactivity; import android.app.ListActivity; import android.os.Bundle; public class MyListActivity extends ListActivity { public void onCreate(Bundle icicle) { super.onCreate(icicle); String[] values = new String[] { "Android", "iPhone", "WindowsMobile", "Blackberry", "WebOS", "Ubuntu", "Windows7", "Max OS X", "Linux", "OS/2" }; MySimpleArrayAdapter adapter = new MySimpleArrayAdapter(this, values); setListAdapter(adapter); } }
If you run this example you should get a list with different icons for the certain elements.
