This is the fourth blog post in series of posts that will demonstrate how Android ListView can be used in your application. We started the series off with the most basic loading of hard coded arraylist in a ListView. In the second post we loaded the data from the Xml file. In the third post, we loaded the listview from an Xml resource file and displayed it as multiple selection checkboxes which user can select and unselect and also save/load their selections.
In this post we will take it one step further and bind the ListView with a custom layout for single row. The single row layout will have a Checkbox, a TextView and an ImageButton to demonstrate how these three can be repeated on each row. We will still load the data from Xml file and save the user selection in Sharedpreferences.
To keep it easy to understand we are still not doing any database operations. We will tackle loading data from Sqlite databse instead of doing it from Xml file in upcoming posts.
Skill Assumptions:
- You have configured your Android development environment on Eclipse or other tools
- You know how to create a new Android project
- You have gone through the example code for the previous posts in the series
What you will learn:
- How to load a ListView based on Xml resource file
- How to use a custom layout for single row to display CheckBox, TextView and ImageButton for each item in the list and allow user to select multiple items
- How to implement a custom Adapter by extending ArrayAdapter and bind a it to the ListView and allow user to select multiple items
- How to save user selections to SharedPreferences so that the selected items can be loaded back as checked items when the activity starts again
- How to handle onClick event so that selections are saved user clicks on each individual item in ListView
What are we doing in this project:
- Create an Activity [Start.java] with layout containing one button
- When the button is clicked we will launch another activity [CustomListView.java] that will load the listview from Xml resource file using custom adapter derived from ArrayAdapter that binds with custom layout for single row
- When user clicks each item to check the box, we will save the items that are checked in SharedPreferences
- When user clicks Clear, we will clear the selection on listview and saved items
- When user clicks back button or restarts the application, we will load any previously checked items from the SharedPreferences and set them as checked
Lets jump into the second example.
Step 1 – Create a new Eclipse project
Step 2 -Â Add a default Activity named Start which will add main.xml under layout folder and Start.java under your namespace folder under src.
Step 3 – In your eclipse project under the res folder create a new subfolder named xml [all lowercase]. Create a new Xml file named todolist.xml in your res/xml folder using your favorite editor with following content pasted in it. Save the file and refresh your res folder in Eclipse. This step is same as second post.
Here is how your folder structure will look like:

// layout/todolist.xml
Step 4 – Copy paste following Xml on your main.xml file.
// layout/main.xml
Step 5 – Copy paste following code in your AndroidManifest.xml file. The most important attribute is to android:theme=”@android:style/Theme.NoTitleBar” which will disable the default Android title bar because we are replacing that with our own Action bar.
Also, notice the additional activity node we have added that registers the activity with Android – activity android:name=”.CustomListView”. We will add the CustomeListView class and related layout in next steps.
// AndroidManifest.xml>
Step 6 – Copy and paste following code in your Start.java class file.
// src/Start.java
package com.appfulcrum.blog.examples.listviewcustom;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
public class Start extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button btnSimple = (Button) findViewById(R.id.btnSimple);
btnSimple.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Toast.makeText(getApplicationContext(),
" You clicked Custom ListView button", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(v.getContext(), CustomListView.class);
startActivityForResult(intent, 0);
}
});
}
}
Step 7 – Add a new Android layout file in your res/layout folder named simple.xml. Then copy and paste following xml in simple.xml file.
// res/layout/simple.xml
Step 8 – Add a new Android layout file in your res/layout folder named single_item.xml. Then copy and paste following xml in single_item.xml file.
// res/layout/single_item.xml
Step 9 – Add a new class file named CustomListView.java to your project in your src folder. Then copy and paste following code in the SimpleListView.java file. Notice the resource id – android.R.layout.single_item.
// src/CustomListView.java
package com.appfulcrum.blog.examples.listviewcustom;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import org.xmlpull.v1.XmlPullParserException;
import android.app.Activity;
import android.app.ListActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.Toast;
import android.content.*;
import android.content.res.XmlResourceParser;
public class CustomListView extends ListActivity {
private ListView mainListView = null;
CustomToDoListAdapter customTODOAdapter = null;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.simple);
Button btnClear = (Button) findViewById(R.id.btnClear);
btnClear.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Toast.makeText(getApplicationContext(),
" You clicked Clear button", Toast.LENGTH_SHORT).show();
ClearSelections();
}
});
// Prepare an ArrayList of todo items
ArrayList listTODO = PrepareListFromXml();
this.mainListView = getListView();
mainListView.setCacheColorHint(0);
// Bind the data with the list
this.customTODOAdapter = new CustomToDoListAdapter(CustomListView.this,
R.layout.single_item, listTODO);
mainListView.setAdapter(this.customTODOAdapter);
mainListView.setItemsCanFocus(false);
mainListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
}
private void ClearSelections() {
for (int i = 0; i < this.customTODOAdapter.getCount(); i++) {
LinearLayout v = (LinearLayout) this.mainListView.getChildAt(i);
CheckBox chk = (CheckBox) v.getChildAt(0);
chk.setChecked(false);
}
this.customTODOAdapter.ClearSelections();
}
private ArrayList PrepareListFromXml() {
ArrayList todoItems = new ArrayList();
XmlResourceParser todolistXml = getResources().getXml(R.xml.todolist);
int eventType = -1;
while (eventType != XmlResourceParser.END_DOCUMENT) {
if (eventType == XmlResourceParser.START_TAG) {
String strNode = todolistXml.getName();
if (strNode.equals("item")) {
todoItems.add(todolistXml.getAttributeValue(null, "title"));
}
}
try {
eventType = todolistXml.next();
} catch (XmlPullParserException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return todoItems;
}
}
Step – 10 Add a new class file named CustomToDoListAdapter.java to your project in your src folder. Then copy and paste following code in the CustomToDoListAdapter.java file.
Few important things to notice in this class:
1. It extends ArrayAdapter
2. It implements OnClickListener which we bind with Click event for checkbox within each row
3. It overrides getView method and sets the value for the TextView and state of the CheckBox from the SharedPreferences
// src/CustomToDoListAdapter.java package com.appfulcrum.blog.examples.listviewcustom; import java.util.ArrayList; import java.util.Arrays; import android.content.Context; import android.content.SharedPreferences; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.View.OnClickListener; import android.widget.ArrayAdapter; import android.widget.CheckBox; import android.widget.TextView; public class CustomToDoListAdapter extends ArrayAdapterimplements OnClickListener { private ArrayList todoItems; private Context context; private ArrayList selectedItems = new ArrayList (); final String SETTING_TODOLIST = "todolist"; public CustomToDoListAdapter(Context context, int textViewResourceId, ArrayList dataItems) { super(context, textViewResourceId, dataItems); this.context = context; this.todoItems = dataItems; LoadSelections(); } @Override public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub View v = convertView; if (v == null) { LayoutInflater inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); v = inflater.inflate(R.layout.single_item, null); } String itemText = this.todoItems.get(position); TextView bTitle = (TextView) v.findViewById(R.id.txtTitle); CheckBox bCheck = (CheckBox) v.findViewById(R.id.bcheck); bCheck.setTag(itemText); if (this.selectedItems.contains(itemText)) bCheck.setChecked(true); bCheck.setOnClickListener(this); bTitle.setText(itemText); return (v); } @Override public void onClick(View v) { CheckBox cBox = (CheckBox) v; String itemText = (String) cBox.getTag(); Log.d("debug", "message from click listener"); if (cBox.isChecked()) { if (!this.selectedItems.contains(itemText)) this.selectedItems.add(itemText); } else { if (this.selectedItems.contains(itemText)) this.selectedItems.remove(itemText); } SaveSelections(); } public void ClearSelections() { this.selectedItems.clear(); SaveSelections(); } private String getSavedItems() { String savedItems = ""; for (int i = 0; i < selectedItems.size(); i++) { if (savedItems.length() > 0) { savedItems += "," + this.selectedItems.get(i); } else { savedItems += this.selectedItems.get(i); } } return savedItems; } private void SaveSelections() { // save the selections in the shared preference in private mode for the // user SharedPreferences settingsActivity = this.context.getSharedPreferences( SETTING_TODOLIST, android.content.Context.MODE_PRIVATE); SharedPreferences.Editor prefEditor = settingsActivity.edit(); String savedItems = getSavedItems(); prefEditor.putString(SETTING_TODOLIST, savedItems); prefEditor.commit(); } private void LoadSelections() { // if the selections were previously saved load them SharedPreferences settingsActivity = this.context.getSharedPreferences( SETTING_TODOLIST, android.content.Context.MODE_PRIVATE); if (settingsActivity.contains(SETTING_TODOLIST)) { String savedItems = settingsActivity .getString(SETTING_TODOLIST, ""); this.selectedItems.addAll(Arrays.asList(savedItems.split(","))); } } }
Step 11 – Run the application as Android Application and you should see below for output.

