Version 3.1
Copyright © 2010, 2011, 2012 Lars Vogel
05.02.2012
| Revision History | ||
|---|---|---|
| Revision 0.1 | 01.07.2010 | Lars Vogel |
| Created | ||
| Revision 0.2 - 3.1 | 03.07.2010 - 05.02.2012 | Lars Vogel |
| bug fixes and enhancements | ||
Table of Contents
The display of elements in a lists is a very common pattern in mobile applications. The user gets a list of items and can scroll through them. If he selects one item this usually triggers something else.
Android provides the
ListView
class
which is capable of displaying a scrollable list of items. These
items can be of any type.
For example the following provides a layout file with includes a
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="@+id/mylist" android:layout_width="match_parent" android:layout_height="wrap_content" > </ListView> </LinearLayout>
A
ListView
gets its data assigned to via an an adapter. This adapter
also defines
which layout is used for the individual rows of
the list and how the
data is assigned to the
Views
in the row layout.
The adapter is assigned to the
list
via the
setAdapter
method on the
ListView
object.
An adapter which
must extend
BaseAdapter.
Android provides several
standard adapters; the most important are
ArrayAdapter
and
CursorAdapter.
ArrayAdapter
can handle data based on Arrays or Lists while
SimpleCursorAdapter
handle database related data.
Android provides already some default layouts for rows in
ListViews
which can be used in an adapter, e.g.
android.R.layout.simple_list_item1.
The following shows an example of using
ArrayAdapter
in an Activity assuming that the layout contains a ListView with the
"mylist" ID.
ListView listView = (ListView) findViewById(R.id.mylist); String[] values = new String[] { "Android", "iPhone", "WindowsMobile", "Blackberry", "WebOS", "Ubuntu", "Windows7", "Max OS X", "Linux", "OS/2" }; // First paramenter - Context // Second parameter - Layout for the row // Third parameter - ID of the View to which the data is written // Forth - the Array of data ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, android.R.id.text1, values); // Assign adapter to ListView listView.setAdapter(adapter);
The
ArrayAdapter
class can handle any Java object as input. Per default it
will call
the
toString()
method on each object for the data which should be displayed.
In the constructor of
ArrayAdapter
your can optionally specify a resource ID to a
View
to
which the data should be will assigned. This optional, if
nothing is
specified the adapter use the
android.R.id.text1
ID
as default.
ArrayAdapter
assigns the output of the
toString()
to this
View.
To influence what data is displayed in which field of the row, you
can
create your own
Adapter, e.g. by extending the
ArrayAdapter
class.
Your row layout can also contain
Views
which interact with the underlying data model. For example you can
have
a
Checkbox
in your row layout and if the
Checkbox
is selected you
change the data which
is displayed in the row.
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(); } });
You can develop your own
adapter by
extending existing adapter
implementations or by sub-classing the
BaseAdapter
class directly.
The
getView()
method is called for every line in the
ListView
and
determines the layout and the data assignment
of the row.
This method is
responsible
for creating the individual rows of
your
ListView.
The
getView()
method returns a
View
for each row.
This
View
is typically a Layout (
ViewGroup
) and contains several other
Views, e.g. an
ImageView
and a
TextView.
Within the
getView()
method you would typically inflate an XML based layout and then set
the
values of the individual
Views
in the layout based on the data for the row. The individual
elements in the layout can be found via the
findViewById()
method call. To optimize performance you would buffer data
and reuse existing rows if possible. We cover that later.
To extract an layout from an XML resource in the
getView()
method you can use the system service
LayoutInflator. This service can get accessed via the
Activity
or via the
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)
method call.
The following shows an implementation of an own adapter. This adapter assumes that you have two png files (no.png and yes.png) in your drawable* folder. If this extract the layout for the rows, assigned the data to the text field, and if the entries starts with "IPhone" is will display the no.png image.
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; } }
The
ListActivity
class which extends
Activity
was designed to simplify the handling of lists.
It contains a default
ListView
and defines the
onListItemClick()
method for handling selection of list items.
Internally the
ListActivity
registers an
OnItemClickListener
on the
ListView.
ListActivity
allows to set the adapter to the
ListView
via the
setListAdapter()
method.
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); } }
An
ListActivity
has per default a layout available which includes a default
ListView. Therefore you are not required to use the
setContentView()
to assign a layout to your
Activity
method.
In case you need more the just a
ListView
in your
Activity, you can use you own layout for
ListActivity.
In this case
your
layout must have an
ListView
element with the
android:id
attribute set to
@android:id/list.
For
example:
<ListView android:id="@+android:id/list" android:layout_width="match_parent" android:layout_height="wrap_content" > </ListView>
You
can also use a
view with the id
@android:id/empty.
This
view
is
displayed if the
list is empty. For example you could
display
here an
error message.
In a
ListView
not all rows are visible at the same time. If the user scrolls the
list then certain rows (and their associated views) will not be
visible anymore.
Every
View
which get inflated from the XML resources will result in a Java
object. Creating Java objects is expensive with regards to time and
memory
consumption.
Not all rows are displayed at the same time, therefore
Android
recycles
rows (
Views
) which are not displayed anymore and allows that these
rows are
reused. If it has a recycled row, it passes
these rows to
getView()
method as
convertView
parameter.
A performance optimized adapter assigns the new content to the recycled row. Only updating the content of the row avoids loading the XML layout. That is a big performance saver, as reading XML files is an expensive operation.
Your adapter implementation can re-use this view and can avoid inflating a layout for the current row. This saves memory and CPU consumption. It just need to set the new values into the existing layout.
convertView
may be
NULL
if there is no row to recycle, your
implementation need to check
this.
Via the parameter
convertView
in the
getView()
method you can re-use an existing row and fill the
Views
of this row with new data.
If this
convertView
is not null you can re-use it.
The
findViewById()
method is an expensive operation, therefore we should avoid doing
this
operation if not necessary. You can use the "View Holder""
pattern for this.
A ViewHolder class is a static class in your adapter which allows to
hold
references to the fields in your layout so that you can avoid
using
findViewById()
on your layout.
The ViewHolder stores a reference to the required views in a
row.
This
ViewHolder is then attached to the row via the
setTag()
method. Every view can get a tag assigned. If the row is
recycled we
can get the ViewHolder via
getTag()
method.
This is much faster then the
repetitive call of the
findViewById()
method.
The default Android adapter like
ArrayAdapter
is already performance optimized, if you are using this class you
don't have to do anything. But if you write your own Adapter
you have
to take that into consideration.
package de.vogella.android.listactivity; import android.app.Activity; 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 MyPerformanceArrayAdapter extends ArrayAdapter<String> { private final Activity context; private final String[] names; static class ViewHolder { public TextView text; public ImageView image; } public MyPerformanceArrayAdapter(Activity context, String[] names) { super(context, R.layout.rowlayout, names); this.context = context; this.names = names; } @Override public View getView(int position, View convertView, ViewGroup parent) { View rowView = convertView; if (rowView == null) { LayoutInflater inflater = context.getLayoutInflater(); rowView = inflater.inflate(R.layout.rowlayout, null); ViewHolder viewHolder = new ViewHolder(); viewHolder.text = (TextView) rowView.findViewById(R.id.TextView01); viewHolder.image = (ImageView) rowView .findViewById(R.id.ImageView01); rowView.setTag(viewHolder); } ViewHolder holder = (ViewHolder) rowView.getTag(); String s = names[position]; holder.text.setText(s); if (s.startsWith("Windows7") || s.startsWith("iPhone") || s.startsWith("Solaris")) { holder.image.setImageResource(R.drawable.no); } else { holder.image.setImageResource(R.drawable.ok); } return rowView; } }
The following will demonstrate how to use a
ListView
in an
ListActivity
the
ArrayAdapter
class
and a predefined layout from Android.
Create a new Android project called
de.vogella.android.listactivity
with the
Activity
called
MyListActivity.
Change
MyListActivity
to the following. 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. You use a different constructor to
identify the
View
to which the
ArrayAdapter
assigns the text.
If this ID is
not provides Android searches for an
element with the
@android:id/text1
ID in the layout of the row.
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 own 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 don't 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.

The following will implement a performance optimized version of the adapter from the previous example.
Create the following
MyPerformanceArrayAdapter
class.
package de.vogella.android.listactivity; import android.app.Activity; 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 MyPerformanceArrayAdapter extends ArrayAdapter<String> { private final Activity context; private final String[] names; static class ViewHolder { public TextView text; public ImageView image; } public MyPerformanceArrayAdapter(Activity context, String[] names) { super(context, R.layout.rowlayout, names); this.context = context; this.names = names; } @Override public View getView(int position, View convertView, ViewGroup parent) { View rowView = convertView; if (rowView == null) { LayoutInflater inflater = context.getLayoutInflater(); rowView = inflater.inflate(R.layout.rowlayout, null); ViewHolder viewHolder = new ViewHolder(); viewHolder.text = (TextView) rowView.findViewById(R.id.TextView01); viewHolder.image = (ImageView) rowView .findViewById(R.id.ImageView01); rowView.setTag(viewHolder); } ViewHolder holder = (ViewHolder) rowView.getTag(); String s = names[position]; holder.text.setText(s); if (s.startsWith("Windows7") || s.startsWith("iPhone") || s.startsWith("Solaris")) { holder.image.setImageResource(R.drawable.no); } else { holder.image.setImageResource(R.drawable.ok); } return rowView; } }
Use your new adapter in your
Activity. If you run the application it should look the same but it will be
much faster, especially for large datasets.
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" }; setListAdapter(new MyPerformanceArrayAdapter(this, values)); } }
You can use the
SimpleAdapter
class to show the data of two elements. This class expects a Array of
Strings (
from
data) in which the fields of the input data are defined. It also
requires a Array of ints which defines the ID's of the widgets in the
layout for the row to which these fields are mapped.
The actual data is then a list of Maps. The Map defines for each field in the from data a value.
The following shows an example which reuses an predefined layout from Android for the row.
package de.vogella.android.listactivity; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import android.app.ListActivity; import android.os.Bundle; import android.widget.SimpleAdapter; public class MyTwoListItemsActivity extends ListActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ArrayList<Map<String, String>> list = buildData(); String[] from = { "name", "purpose" }; int[] to = { android.R.id.text1, android.R.id.text2 }; SimpleAdapter adapter = new SimpleAdapter(this, list, android.R.layout.simple_list_item_2, from, to); setListAdapter(adapter); } private ArrayList<Map<String, String>> buildData() { ArrayList<Map<String, String>> list = new ArrayList<Map<String, String>>(); list.add(putData("Android", "Mobile")); list.add(putData("Windows7", "Windows7")); list.add(putData("iPhone", "iPhone")); return list; } private HashMap<String, String> putData(String name, String purpose) { HashMap<String, String> item = new HashMap<String, String>(); item.put("name", name); item.put("purpose", purpose); return item; } }
The following example will demonstrate how to use standard Java
object
and how to interact from the
Views
of the row with the model.
We still use the same project.
Create the following
Model
which hold the name and the information if this element is
currently
selected.
package de.vogella.android.listactivity; public class Model { private String name; private boolean selected; public Model(String name) { this.name = name; selected = false; } public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isSelected() { return selected; } public void setSelected(boolean selected) { this.selected = selected; } }
Create a new layout file called "rowbuttonlayout.xml".
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" > <TextView android:id="@+id/label" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@+id/label" android:textSize="30px" > </TextView> <CheckBox android:id="@+id/check" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_marginLeft="4px" android:layout_marginRight="10px" > </CheckBox> </RelativeLayout>
Create the following Adapter. This adapter adds a listener
on the
Checkbox. If
Checkbox
is selected the underlying data of
the model is changed.
Checkbox
gets the corresponding model element
assigned via the setTag() method.
package de.vogella.android.listactivity; import java.util.List; import android.app.Activity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.TextView; public class InteractiveArrayAdapter extends ArrayAdapter<Model> { private final List<Model> list; private final Activity context; public InteractiveArrayAdapter(Activity context, List<Model> list) { super(context, R.layout.rowbuttonlayout, list); this.context = context; this.list = list; } static class ViewHolder { protected TextView text; protected CheckBox checkbox; } @Override public View getView(int position, View convertView, ViewGroup parent) { View view = null; if (convertView == null) { LayoutInflater inflator = context.getLayoutInflater(); view = inflator.inflate(R.layout.rowbuttonlayout, null); final ViewHolder viewHolder = new ViewHolder(); viewHolder.text = (TextView) view.findViewById(R.id.label); viewHolder.checkbox = (CheckBox) view.findViewById(R.id.check); viewHolder.checkbox .setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { Model element = (Model) viewHolder.checkbox .getTag(); element.setSelected(buttonView.isChecked()); } }); view.setTag(viewHolder); viewHolder.checkbox.setTag(list.get(position)); } else { view = convertView; ((ViewHolder) view.getTag()).checkbox.setTag(list.get(position)); } ViewHolder holder = (ViewHolder) view.getTag(); holder.text.setText(list.get(position).getName()); holder.checkbox.setChecked(list.get(position).isSelected()); return view; } }
Finally change your
Activity
to the following.
package de.vogella.android.listactivity; import java.util.ArrayList; import java.util.List; import android.app.ListActivity; import android.os.Bundle; import android.widget.ArrayAdapter; public class MyList extends ListActivity {/** Called when the activity is first created. */public void onCreate(Bundle icicle) { super.onCreate(icicle); // Create an array of Strings, that will be put to our ListActivity ArrayAdapter<Model> adapter = new InteractiveArrayAdapter(this, getModel()); setListAdapter(adapter); } private List<Model> getModel() { List<Model> list = new ArrayList<Model>(); list.add(get("Linux")); list.add(get("Windows7")); list.add(get("Suse")); list.add(get("Eclipse")); list.add(get("Ubuntu")); list.add(get("Solaris")); list.add(get("Android")); list.add(get("iPhone")); // Initially select one of the items list.get(1).setSelected(true); return list; } private Model get(String s) { return new Model(s); } }
If you start your app you should be able to flag items. These changes will be reflected in your model.

You can also add a
LongItemClickListener
to the
View. For this receive the
ListView
via the
getListVIew()
method
and set the
LongItemClickListener
via the
setOnItemLongClickListener() method.
package de.vogella.android.listactivity; import android.app.ListActivity; import android.os.Bundle; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemLongClickListener; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.Toast; public class MyList extends ListActivity {/** Called when the activity is first created. */public void onCreate(Bundle icicle) { super.onCreate(icicle); // Create an array of Strings, that will be put to our ListActivity String[] names = new String[] { "Linux", "Windows7", "Eclipse", "Suse", "Ubuntu", "Solaris", "Android", "iPhone" }; ArrayAdapter<String> adapter = new MyPerformanceArrayAdapter(this, names); setListAdapter(adapter); ListView list = getListView(); list.setOnItemLongClickListener(new OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { Toast.makeText(MyList.this, "Item in position " + position + " clicked", Toast.LENGTH_LONG).show(); // Return true to consume the click event. In this case the // onListItemClick listener is not called anymore. return true; } }); } @Override protected void onListItemClick(ListView l, View v, int position, long id) { super.onListItemClick(l, v, position, id); // Get the item that was clicked Object o = this.getListAdapter().getItem(position); String keyword = o.toString(); Toast.makeText(this, "You selected: " + keyword, Toast.LENGTH_SHORT) .show(); } }
You can also support single and multi selection. See the following snippets for examples. To get the selected item(s) use listView.getCheckedItemPosition() or listView.getCheckedItemPositions(). If you have stable ID you could also use listView.getCheckedItemIds() to get the selected ids.
package de.vogella.android.listactivity; import android.app.ListActivity; import android.os.Bundle; import android.widget.ArrayAdapter; import android.widget.ListView; public class MyList extends ListActivity {/** Called when the activity is first created. */public void onCreate(Bundle icicle) { super.onCreate(icicle); // Create an array of Strings, that will be put to our ListActivity String[] names = new String[] { "Linux", "Windows7", "Eclipse", "Suse", "Ubuntu", "Solaris", "Android", "iPhone", "Linux", "Windows7", "Eclipse", "Suse", "Ubuntu", "Solaris", "Android", "iPhone" }; setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_multiple_choice, android.R.id.text1, names)); ListView listView = getListView(); listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); } }
package de.vogella.android.listactivity; import android.app.ListActivity; import android.os.Bundle; import android.widget.ArrayAdapter; import android.widget.ListView; public class MyList extends ListActivity {/** Called when the activity is first created. */public void onCreate(Bundle icicle) { super.onCreate(icicle); // Create an array of Strings, that will be put to our ListActivity String[] names = new String[] { "Linux", "Windows7", "Eclipse", "Suse", "Ubuntu", "Solaris", "Android", "iPhone", "Linux", "Windows7", "Eclipse", "Suse", "Ubuntu", "Solaris", "Android", "iPhone" }; setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_single_choice, android.R.id.text1, names)); ListView listView = getListView(); listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); } }
You can of course put arbitray elements around your ListView. For example you can define a layout with two TextViews and a ListView between them. If you do this, you must assign the id "@android:id/list" to the ListView, as the ListActivity searches for a view with this id. If you do this then one TextView will always be visible above the List (header) and the other will be visible below the ListView. If you want to display the header / footer view only if see the beginning / end of the list you can use view.setHeaderView() or view.setFooterView(). For example:
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; public class MyList extends ListActivity {/** Called when the activity is first created. */public void onCreate(Bundle icicle) { super.onCreate(icicle); // Create an array of Strings, that will be put to our ListActivity String[] names = new String[] { "Linux", "Windows7", "Eclipse", "Suse", "Ubuntu", "Solaris", "Android", "iPhone", "Linux", "Windows7", "Eclipse", "Suse", "Ubuntu", "Solaris", "Android", "iPhone" }; View header = getLayoutInflater().inflate(R.layout.header, null); View footer = getLayoutInflater().inflate(R.layout.footer, null); ListView listView = getListView(); listView.addHeaderView(header); listView.addFooterView(footer); setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_single_choice, android.R.id.text1, names)); } }
In case you work with a content provider or directly with the database
you can use the
SimpleCursorAdapter
to define the data for your
ListView. The following will demonstrates how to access the Contacts
ContentProvider.
Create a new Android project called
"de.vogella.android.listactivity.cursor"
with the
Activity
called
"MyListActivity". Change
MyListActivity
to the following.
package de.vogella.android.listactivity.cursor; import android.app.ListActivity; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.provider.ContactsContract; import android.widget.ListAdapter; import android.widget.SimpleCursorAdapter; public class MyListActivity extends ListActivity {/** Called when the activity is first created. */@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Cursor mCursor = getContacts(); startManagingCursor(mCursor); // Now create a new list adapter bound to the cursor. // SimpleListAdapter is designed for binding to a Cursor. ListAdapter adapter = new SimpleCursorAdapter(this, // Context. android.R.layout.two_line_list_item, // Specify the row template // to use (here, two // columns bound to the // two retrieved cursor // rows). mCursor, // Pass in the cursor to bind to. // Array of cursor columns to bind to. new String[] { ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME }, // Parallel array of which template objects to bind to those // columns. new int[] { android.R.id.text1, android.R.id.text2 }); // Bind to our new adapter. setListAdapter(adapter); } private Cursor getContacts() { // Run query Uri uri = ContactsContract.Contacts.CONTENT_URI; String[] projection = new String[] { ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME }; String selection = ContactsContract.Contacts.IN_VISIBLE_GROUP + " = '" + ("1") + "'"; String[] selectionArgs = null; String sortOrder = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"; return managedQuery(uri, projection, selection, selectionArgs, sortOrder); } }
Make sure you give your application the permission to read the contacts. (Uses Permissions "android.permission.READ_CONTACTS" in AndroidManifest.xml)
Before posting questions, please see the vogella FAQ. If you have questions or find an error in this article please use the www.vogella.de Google Group. I have created a short list how to create good questions which might also help you.
Eclipse RCP Training (German) Eclipse RCP Training with Lars Vogel
Android Tutorial Introduction to Android Programming
GWT Tutorial Program in Java and compile to JavaScript and HTML
Eclipse RCP Tutorial Create native applications in Java
JUnit Tutorial Test your application
Git Tutorial Put everything you have under distributed version control system