Android ListView and ListActivity
This tutorial describes how to use ListView and ListActivity in Android. The tutorial is based on Eclipse 3.7, Java 1.6 and Android 4.0 (Ice Cream Sandwich).
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 typically triggers a detailed screen for the selection.
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 listing shows 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="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <ListView android:id="@+id/mylist" android:layout_width="match_parent" android:layout_height="wrap_content" > </ListView> </LinearLayout>
A
ListView
receives its data via an adapter. The adapter also defines how each row is the ListView
is displayed .
The adapter is assigned to the list via the
setAdapter
method on the ListView
object.ListView
calls the getView()
method on the adapter for each data element. In this method the adapter determines the layout of the row and how the data is mapped to the Views
in this layout.
This
View
is typically a ViewGroup
(Layout) and contains several other Views
, e.g. an ImageView
and a TextView
.
An adapter extend the
BaseAdapter
class. Android provides several standard adapters; 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 Android platform provides also default layouts for rows in
ListViews
. For exampleandroid.R.layout.simple_list_item1
.
The following coding shows an example of the usage of the
ArrayAdapter
class and a predefined layout from the Android system. The example assumes that layout of the Activity
contains aListView
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 TextView 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. It maps the data of this input to aTextView
in the layout. By default ArrayAdapter
uses the android.R.id.text1
ID, but you can define another one in the ArrayAdapter
constructor.ArrayAdapter
uses the toString()
method of the data input object to determine what is shown in the View
.
To control the data assignment and to support this assignment to several
Views
, you create your ownAdapter
implementation. For this you would extend an existing adapter implementations or by sub-classing the BaseAdapter
class directly.
Within the
getView()
method you would inflate an XML based layout and then set the values of the individual Views
in the layout. For inflating an XML layout you can use the system serviceLayoutInflator
. This service can get accessed via the Activity
or via thecontext.getSystemService(Context.LAYOUT_INFLATER_SERVICE)
method call.
The individual elements in the layout can be found via the
findViewById()
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 one of your
res/drawable
folders. The coding inflates an XML layout file, finds the relevant Views
in the layout and sets the 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; } }
Your row layout can also contain
Views
which interact with the underlying data model via the adapter. For example you can have a Checkbox
in your row layout and if the Checkbox
is selected, the underlying data is changed.
The
ArrayAdapter
class allows to remove all elements in its underlying data structure with theclear()
method call. You can then add new elements via the add()
method or a Collection via theaddAll()
method.
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(); } });
Filtering and sorting is handled by the Adapter. You need to implement the logic in your custom Adapter implementation.
ListView
supports filtering of elements via its adapter.
For example the
ArrayAdapter
class implements the Filterable
interface and contains a default filter implementation called ArrayFilter
as inner class .
This default implementation allows to filter based on String, via
adapter.getFilter().filter(searchString)
.
Typically you want to add a
EditText
field to your layout and attach a TextChangeListener to it.EditText filterEditText = (EditText) findViewById(R.id.filterText); filterEditText.addTextChangedListener(new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { adapter.getFilter().filter(s.toString()); } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void afterTextChanged(Editable s) { } });
Typical you would implement your own filter logic in your adapter.
The
ListActivity
class which extends the Activity
class was designed to simplify the handling ofListViews
.
It you do not assign a layout to a
ListActivity
it contains a default ListView
and defines theonListItemClick()
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); } }
ListActivity
does not require that you assign a layout to it via the setContentView()
method, if you only want to show a ListView
ListActivity
contains by default a ListView
.
In case you need to include more
Views
than a ListView
in your ListActivity
you can still assign a layout to your Activity
. In this case your layout must contain a ListView
with the android:id
attribute set to @android:id/list
.<ListView android:id="@android:id/list" android:layout_width="match_parent" android:layout_height="wrap_content" > </ListView>
If you use a
View
with the @android:id/empty
ID in your layout, ListActivity
will automatically show this View
if the ListView
is empty and hide it otherwise. For example you could display here an error message.
Every
View
which get inflated from an XML layout file will result in a Java object. Creating Java objects is expensive with regards to time and memory consumption.
In addition using the
findViewById()
method is relatively time consuming, even though it is not as bad as XML inflating.
This part describes how to reduce these operations to make your
ListView
faster.
A
ListView
typically contain more data then the number of displayed rows. If the user scrolls the list then rows and their associated Views
will be 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
View
which represents a row is not visible anymore it allows the getView()
method to reuse it via the convertView
parameter.
A performance optimized adapter assigns the new data to the
convertView
. This avoids inflating an XML file and creating new Java objects.
In case no
View
is available for reuse, Android will pass null
to the convertView
parameter. Therefore the adapter implementation need to check for this.
The "View Holder" pattern allows to avoid the
findViewById()
method call for a reusedconvertView
.
A ViewHolder class is a static inner class in your adapter which hold references to the relevant
Views
in your layout. This reference is assigned to the View
which represent the row layout as a tag via thesetTag()
method.
If we receive a
convertView
object, we can get the instance of the ViewHolder
via the getTag()
method and assign the new attributes to the Views
via the ViewHolder
reference.
While this sounds complex this is approx. 15 % faster then using the
findViewById()
method.
The default Android adapters like
ArrayAdapter
is already performance optimized and you do not need to change them.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; } }
For following assumes that you already familiar with the concept of the
ActionBar
and contextual action mode in general. In part will explain how to use contextual action mode for a ListView
selection.
To assign a contextual action mode to a long click on a individual item, use the method
setOnItemLongClickListener()
on ListView
. This methods includes information about the selected item. In this method you can start the ActionMode.
The following examples demonstrates that, it assumes that you have a menu XML file defined called "rowselection.xml" and that this menu contains one entry with the "@+id/menuitem1_show" ID.
package de.vogella.android.listactivity; import android.app.ListActivity; import android.os.Bundle; import android.view.ActionMode; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemLongClickListener; import android.widget.Toast; public class MyListActivityActionbar extends ListActivity { protected Object mActionMode; public int selectedItem = -1; public void onCreate(Bundle icicle) { super.onCreate(icicle); setContentView(R.layout.main); 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" }; MySimpleArrayAdapter adapter = new MySimpleArrayAdapter(this, values); setListAdapter(adapter); getListView().setOnItemLongClickListener(new OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { if (mActionMode != null) { return false; } selectedItem = position; // Start the CAB using the ActionMode.Callback defined above mActionMode = MyListActivityActionbar.this .startActionMode(mActionModeCallback); view.setSelected(true); return true; } }); } private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() { // Called when the action mode is created; startActionMode() was called public boolean onCreateActionMode(ActionMode mode, Menu menu) { // Inflate a menu resource providing context menu items MenuInflater inflater = mode.getMenuInflater(); // Assumes that you have "contexual.xml" menu resources inflater.inflate(R.menu.rowselection, menu); return true; } // Called each time the action mode is shown. Always called after // onCreateActionMode, but // may be called multiple times if the mode is invalidated. public boolean onPrepareActionMode(ActionMode mode, Menu menu) { return false; // Return false if nothing is done } // Called when the user selects a contextual menu item public boolean onActionItemClicked(ActionMode mode, MenuItem item) { switch (item.getItemId()) { case R.id.menuitem1_show: show(); // Action picked, so close the CAB mode.finish(); return true; default: return false; } } // Called when the user exits the action mode public void onDestroyActionMode(ActionMode mode) { mActionMode = null; selectedItem = -1; } }; private void show() { Toast.makeText(MyListActivityActionbar.this, String.valueOf(selectedItem), Toast.LENGTH_LONG).show(); } }
If you start your application and long press on an item in the list, you get your contextual
ActionBar
menu.
The following will demonstrate how to use a
ListView
in an ListActivity
with the predefinedArrayAdapter
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
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 theView
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 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.
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 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.
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 thegetListVIew()
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(); } }
To get the selected item(s) of a
ListView
use the getCheckedItemPosition()
for a single selection method or listView.getCheckedItemPositions()
for multiple selections. . If you have stable ID you could also use the getCheckedItemIds()
method to get the selected IDs.
By default a
ListView
supports only the single selection of a row, but you can activate multi selection via a flag. See the following snippets for examples.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
calledMyListActivity. 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)
No comments:
Post a Comment