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 <code>ListView</code> and the <code>ExpandableListView</code> classes which is capable of displaying a scrollable list of items.
The <code>ExpandableListView</code> 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 <code>ListView</code>.
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 rows in the list view. An adapter extends the<code>BaseAdapter</code> class.
Every line in the list view consists of a layout which can be as complex as you want. A typical line in a list view 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.
The adapter would inflate the layout for each row in its <code>getView()</code> method and assign the data to the individual views in the row.
The adapter is assigned to the <code>ListView</code> via the <code>setAdapter</code> method on the <code>ListView</code> object.
Adapters are not only used by <code>ListView</code>, but also by other views which extend<code>AdapterView</code> as, for example, <code>Spinner</code>, <code>GridView</code>, <code>Gallery</code> and <code>StackView</code>.
Filtering and sorting of the data is handled by the adapter. You need to implement the logic in your custom adapter implementation.
The <code>notifyDataSetChanged()</code> method on the adapter is called if the data has changed or if new data is available.
The <code>notifyDataSetInvalidated()</code> method is called if the data is not available anymore.
To react to selections in the list, set an <code>OnItemClickListener</code> to your <code>ListView</code>.
Android provides default adapter implementations; the most important are <code>ArrayAdapter</code> and <code>CursorAdapter</code>.
<code>ArrayAdapter</code> can handle data based on <code>Arrays</code> or <code>java.util.List</code>.
<code>SimpleCursorAdapter</code> can handle database related data.
The <code>ArrayAdapter</code> 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 <code>toString()</code> method of the object to a view in the row layout.
You can define the ID of the view in the constructor of the <code>ArrayAdapter</code> otherwise the <code>android.R.id.text1</code> ID is used as default.
The <code>ArrayAdapter</code> class allows to remove all elements in its underlying data structure with the <code>clear()</code> method call. You can then add new elements via the <code>add()</code> method or a <code>Collection</code> via the <code>addAll()</code> method.
You can also directly modify the underlying data structure and call the <code>notifyDataSetChanged()</code> method on the adapter to notify it about the changes in data.
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 <code>ArrayList</code> class, but not for arrays.
The following listing shows a layout file called <code>activity_listviewexampleactivity.xml</code> which includes a<code>ListView</code>.
The following example shows the usage of the <code>ListView</code> 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.
The <code>ArrayAdapter</code> is limited as it supports only the mapping of <code>toString()</code> 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 <code>BaseAdapter</code> class directly.
Frequently you extend <code>ArrayAdapter</code> to write a custom adapter, as this is simpler than extending <code>BaseAdapter</code> directly.
The adapter needs to create a layout for each row of the list. The <code>ListView</code> instance calls the <code>getView()</code> 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 <code>ViewGroup</code> (layout manager) and contains several other views , e.g., an <code>ImageView</code>and a <code>TextView</code>. The following graphic shows a list with different layouts for odd and even rows.
Within the <code>getView()</code> 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 <code>LayoutInflator</code> system service.
This layout inflator service can get accessed via the <code>getLayoutInflator()</code> method of the activity or via the <code>context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)</code>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 <code>findViewById()</code> 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 <code>res/drawable</code> folders. The coding inflates an XML layout file, finds the relevant views in the layout and sets their content based on the input data.
The row can also contain views which interact with the underlying data model via the adapter. For example, you can have a <code>Checkbox</code> in your row layout and if the <code>Checkbox</code> is selected, the underlying data is changed.
Android provides specialized fragment and activity classes to simplify list handling.
The classes are the <code>ListActivity</code> class if you want to use lists in activities and the the <code>ListFragment</code> 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 <code>ListView</code> by default. <code>ListActivity</code> and <code>ListFragment</code> also allow you to override a <code>onListItemClick()</code> method for handling selection of list items.
Both classes allow you to set the adapter to the default <code>ListView</code> via the <code>setListAdapter()</code> method.
The following example code shows a simple <code>ListFragment</code> implementation.
The next example code demonstrates the usage of a <code>ListActivity</code>.
You can use a custom layout with <code>ListActivity</code> or <code>ListFragment</code>. In this case the fragment or activity searches in the provided layout for a <code>ListView</code> with the pre-defined <code>android:id</code> attribute set to <code>@android:id/list</code>. This usage is demonstrated by the following code snippet.
If you do not use this ID or do not include a <code>ListView</code> into your layout, the application crashes once you try to display the activity or the fragment.
You can also use a view with the <code>@android:id/empty</code> ID in your layout. The corresponding activity and fragment shows this view automatically if the <code>ListView</code> is empty and hides it otherwise. For example, you could display an error message in such a view.
The following exercise demonstrates how to use a <code>ListView</code> in an <code>ListActivity</code>. You use the predefined<code>ArrayAdapter</code> class and an existing Android layout for the rows.
Create a new Android project called de.vogella.android.listactivity with the activity called <code>MyListActivity</code>.
Change <code>MyListActivity</code> class based on the the following code example. Note that the <code>setContentView()</code> method is not used.
In our example your will define your layout for the rows and use it in your adapter.
Create the <code>rowlayout.xml</code> layout file in the <code>res/layout</code> folder of the <code>de.vogella.android.listactivity</code> project.
Change your activity so that is using the new layout.

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 <code>MySimpleArrayAdapter</code> which will serve as our adapter.
To use this adapter, change the activity to the following.
If you run this example you should get a list with different icons for the certain elements.
Performance is especially important on Android as users expect fast reaction times. Compared to desktop computers an Android device is relatively slow from the hardware perspective.
This part describes how to reduce these operations to implement your custom list adapter efficiently. The default Android adapters like <code>ArrayAdapter</code> are already performance optimized.
Every view which get inflated from an XML layout file will result in a Java object. Inflating layouts and creating Java objects is expensive with regards to time and memory consumption.
In addition using the <code>findViewById()</code> method is relatively time consuming, even though it is not as bad as XML inflating.
A <code>ListView</code> typically contains more data than the number of displayed rows. If the user scrolls the list, then rows and their associated views are being scrolled out of the visible area. The Java objects which represents the rows can be reused for newly visible rows.
If Android determines that a row is not visible anymore, it allows the <code>getView()</code> of the adapter method to reuse the associated view via the <code>convertView</code> parameter.
The adapter can assign new data to the views contained in the view hierarchy of the <code>convertView</code>. This avoids inflating an XML file and creating new Java objects.
In case Android cannot reuse a row, the Android system passes <code>null</code> to the <code>convertView</code> parameter. Therefore the adapter implementation needs to check for this.
The View Holder pattern allows to avoid the <code>findViewById()</code> method in the adapter.
A <code>ViewHolder</code> class is a static inner class in your adapter which holds references to the relevant views. in your layout. This reference is assigned to the row view as a tag via the <code>setTag()</code> method.
If we receive a <code>convertView</code> object, we can get the instance of the <code>ViewHolder</code> via the <code>getTag()</code> method and assign the new attributes to the views via the <code>ViewHolder</code> reference.
While this sounds complex this is approximately 15 % faster then using the <code>findViewById()</code> method.
The following code shows a performance optimized adapter implementation which reuses existing views and implements the holder pattern.
By default a <code>ListView</code> has no selection mode active. You can activate it via the <code>setChoiceMode()</code> method call. Pass<code>ListView.CHOICE_MODE_MULTIPLE</code> for multiple selections or <code>ListView.CHOICE_MODE_SINGLE</code> for single selections to this method.
To get the selected items of a <code>ListView</code>, use the <code>getCheckedItemPosition()</code> for a single selection method or<code>listView.getCheckedItemPositions()</code> for multiple selections. . If you have stable ID, you could also use the<code>getCheckedItemIds()</code> method to get the selected IDs.
Android already provides a default layout for this: the <code>android.R.layout.simple_list_item_multiple_choice</code> layout which contains a configured <code>CheckedTextView</code> view.
The following activities demonstrate how to use these selection modes. If you use these modes, the <code>ListView</code> stores the selected values. It is not persisted in your data model.
The following assumes that you already familiar with the concept of the ActionBar and contextual action mode in general. This part will explain how to use contextual action mode for a <code>ListView</code> selection.
To assign a contextual action mode to a long click on an individual item, use the method<code>setOnItemLongClickListener()</code> on <code>ListView</code>. This methods includes information about the selected item. In this method you can start the <code>ActionMode</code>.
The following examples demonstrate that. It assumes that you have a menu XML file defined called<code>rowselection.xml</code> and that this menu contains one entry with the <code>@+id/menuitem1_show</code> ID.
If you start your application and long press on an item in the list, you get your contextual action bar.
It is good practice to allow the user to undo critical actions. Such a critical action is, for example, the deletion of list items.
A proven pattern to handle this undo option is to offer a selection at the end of the screen. This selection vanishes after a predefined time or once the user continues to interact with the application.
For example, the Gmail application implements such a behavior.
The following description contains an example for implementing an undo action. It uses an animation to phase out the undo button automatically out after a while.
For this example create a new project called com.vogella.android.userinterface.undo based on the BlankTemplatetemplate.
Create the following layout for your activity. It uses a <code>FrameLayout</code> to show two different parts of the user interface. The button bar is initially hidden. The button uses a drawable. Either add such a drawable to your project or remove the reference.
Change your activity so that it is similar to the following code. The Android project wizard in Eclipse already generated an <code>ActionBar</code> entry. This entry is used in the following code. If in doubt, create your own <code>ActionBar</code> entry.
If you select the entry in the ActionBar, the button bar becomes visible for 5 seconds.
The following will implement a performance optimized version of the adapter from the previous example.
Create the following <code>MyPerformanceArrayAdapter</code> class.
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.
You can use the <code>SimpleAdapter</code> class to show the data of two elements. This class expects a Array of Strings (<code>from</code>data) in which the fields of the input data are defined. It also requires a Array of ints which defines the IDs 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.
Frequently you need to select items in your <code>ListView</code>. As the row of the <code>ListView</code> are getting recycled you cannot store the selection on the <code>View</code> level.
Selection is just one possible example but you can imange other interaction between your row and model.
To persist the selection you have to update your data model with the selected state.
To update the data model in your <code>ListView</code> you define your own <code>Adapter</code> class. In this adapter class you attach a listener to the <code>View</code> which is responsible for selecting the model element. If selected you update the state in the model which you can add as a tag to the View to have access to it.
The following example demonstrates how to use standard Java object and how to interact from the <code>Views</code> with the model.
Continue to use the <code>de.vogella.android.listactivity</code> project.
Create the following <code>Model</code> which hold the name and the information if this element is currently selected.
Create the following new layout file called <code>rowbuttonlayout.xml</code>.
Create the following <code>Adapter</code>. This adapter adds a listener on the <code>Checkbox</code> view . If the checkbox is selected the underlying data of the model is changed. <code>Checkbox</code> gets the corresponding model element assigned via the <code>getTag()</code>method.
Finally change your activity to the following.
/** Called when the activity is first created. */
If you start your app you should be able to flag items. These changes will be reflected in your model.
The <code>ExpandableListView</code> is similar to <code>ListView</code> but allow you to define groups and details for this group.<code>ExpandableListView</code> expects and adapter of type <code>BaseExpandableListAdapter</code>.
In this case you have to define two layouts, one for the group and another one for the details row.
In the following example you create an expandable listview similar to the following screenshot.
Create a project called com.vogella.android.listview.expandable with the activity called <code>MainActivity</code>.
Create or adjust the following layout files. First <code>layout/activity_main.xml</code>.
Afterwards create <code>layout/listrow_group.xml</code>.
The last required layout is <code>layout/listrow_details.xml</code>.
Create the following class which hold your domain model for the <code>ExpandableListView</code>.
Finally create the adapter as described by the following listing and change the activity to the code provided below.
You can also add a <code>LongItemClickListener</code> to the <code>View</code>. For this receive the <code>ListView</code> via the <code>getListVIew()</code> method and set the <code>LongItemClickListener</code> via the setOnItemLongClickListener() method.
You can of course put arbitrary <code>Views</code> elements around your ListView. For example you can define a layout with two<code>TextViews</code> and a <code>ListView</code> between them. In this case the two TextViews will always be visible above the List (header) and the other will be visible below the ListView. If you want to display a list header or list footer only at the see the beginning or end of the list you can use the <code>addHeaderView()</code> method or <code>addFooterView()</code> method on the<code>ListView</code> class.
In case you work with a content provider or directly with the database you can use the <code>SimpleCursorAdapter</code> to define the data for your <code>ListView</code>. 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 <code>MyListActivity</code> to the following.
Make sure you give your application the permission to read the contacts. (Uses Permissions "android.permission.READ_CONTACTS" in AndroidManifest.xml)
Sometimes having to press a refresh button on the ActionBar to refresh data can be annoying for the user. Chris Banes has implemented an Open Source library to implement the pull to refresh pattern for a <code>Listview</code>. https://github.com/chrisbanes/Android-PullToRefresh.
,如需轉載請自行聯系原部落客。