Logo Icon Logo
A Crowd-sourced Cookbook on Writing Great Android® Apps
GitHub logo Twitter logo OReilly Book Cover Art

ListView with Icons/images

Author: Wagied Davids
Published? false
FormatLanguage: WikiFormat

Problem:

You want to display data in a ListView with an icon image and some text for each ListView row.

Solution:

This recipe demonstrate how to load data from a XML file, and view the results in a ListView with each row item having an image/icon.

Firstly, the XML resource file must be place in a raw/ directory beneath the res/ folder. In the countries.xml file, all the data is enclosed in the countries and countries tag with each element being enclosed in a country country tag with attributes indicating the name, abbreviation and region of the respective country.

A Java class Country.java is used to model each ListView row item. XML parsing in Android is accomplished by using the built-in SAX parser. In parsing the countries.xml file, the parser CountryParser.java is used to obtain the country names, abbreviations, and regions using the node values extracted from countries.xml. A new Country java object for each XML node which includes the filename of the country image to use.

The above explanation covers the data input from an XML-based file. Now moving to the meat and bones so to speak.

CountryArrayAdapter extends the ArrayAdapter class in which a Context and a list of Country objects are supplied as parameters. The most important function is the getView() function. The getView() function allows the ListView row elements to be altered, in this case the icon image is inserted to the left, and the country name and abbreviation to the right of it.

A simple XML-based View layer listview.xml is created, in which a reference to a countryLV is declared in a ListView element.

For data-binding, a Main.java Android activity is created in which the various elements describe above is brought together. The function setContentView(R.layout.listview) loads the XML view file listview.xml, and extracts a reference to the countryLV ListView holder. The XML parser, called CountryParser loads the data into a Java List containing Country objects.

The custom CountryArrayAdapter is used to override the ListView display so that each row contains the country icon, country name and abbreviation.

Re-wrote this example: 1. xml for listview of countries 2. xml for each row item in listview with place holders for image, country_name, country_abbrev 3. Customized ArrayAdapter for using Country object. 4. Acessing image files in the assets/ directory using: // Set country icon usign File path String imgFilePath = ASSETS_DIR + country.resourceId; try { Bitmap bitmap = BitmapFactory.decodeStream(this.context.getResources().getAssets() .open(imgFilePath)); countryIcon.setImageBitmap(bitmap); } catch (IOException e) { e.printStackTrace(); }

Discussion:

Create a custom ArrayAdapter for modifying visual display of list items.

File: listview.xml - The XML-layout file

<?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">
<ListView
  	android:id="@+id/countryLV"
  	android:layout_width="fill_parent"
  	android:layout_height="fill_parent">
  </ListView>
</LinearLayout>

File: country_listitem.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="horizontal"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content">

	<ImageView
		android:id="@+id/country_icon"
		android:layout_gravity="left"
		android:layout_width="wrap_content"
		android:layout_height="wrap_content" />

	<TextView
		android:id="@+id/country_name"
		android:text="Country Name"
		android:paddingLeft="10dip"
		android:layout_weight="0.5"
		android:layout_gravity="center"
		android:layout_width="wrap_content"
		android:layout_height="wrap_content" />
	<TextView
		android:id="@+id/country_abbrev"
		android:text="Country Abbrev"
		android:layout_gravity="right"
		android:paddingRight="10dip"
		android:layout_width="wrap_content"
		android:layout_height="wrap_content" />

</LinearLayout>

File: countries.xml

<?xml version="1.0" encoding="utf-8"?>
<countries>
		<country name="Australia" abbreviation="au" region="Asia" />
		<country name="Austria" abbreviation="at" region="Europe" />
		<country name="Belgium" abbreviation="be" region="Europe" />
		<country name="Brazil" abbreviation="br" region="S. America" />
		<country name="Canada" abbreviation="ca" region="N. America" />
		<country name="China" abbreviation="cn" region="Asia" />
		<country name="Denmark" abbreviation="dk" region="Europe" />
		<country name="France" abbreviation="fr" region="Europe" />
		<country name="Germany" abbreviation="de" region="Europe" />
		<country name="Hong Kong" abbreviation="hk" region="Asia" />
		<country name="India" abbreviation="in" region="Asia" />
		<country name="Indonesia" abbreviation="id" region="Asia" />
		<country name="Italy" abbreviation="it" region="Europe" />
		<country name="Korea" abbreviation="kr" region="Asia" />
		<country name="Netherlands" abbreviation="nl" region="Europe" />
		<country name="Norway" abbreviation="no" region="Europe" />
		<country name="Portugal" abbreviation="pt" region="Europe" />
		<country name="Singapore" abbreviation="sg" region="Asia" />
		<country name="Spain" abbreviation="es" region="Europe" />
		<country name="Sweden" abbreviation="se" region="Europe" />
		<country name="Switzerland" abbreviation="ch" region="Europe" />
		<country name="Taiwan" abbreviation="tw" region="Asia" />
		<country name="United Kingdom" abbreviation="uk" region="Europe" />
		<country name="United States" abbreviation="us" region="N. America" />
</countries>

File: Main.java

 

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView;

public class Main extends Activity {

	private List<Country> countryList= new ArrayList<Country>();
	
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		// Set the View layer
		setContentView(R.layout.listview);
		setTitle("TestIconizedListView");

		// Create Parser for raw/countries.xml
		CountryParser countryParser = new CountryParser();
		InputStream inputStream = getResources().openRawResource(
				R.raw.countries);
		
		// Parse the inputstream
		countryParser.parse(inputStream);

		// Get Countries
		List<Country> countryList = countryParser.getList();
		
		
		// Create a customized ArrayAdapter
		CountryArrayAdapter adapter = new CountryArrayAdapter(
				getApplicationContext(), R.layout.country_listitem, countryList);
		
		// Get reference to ListView holder
		ListView lv = (ListView) this.findViewById(R.id.countryLV);
		
		// Set the ListView adapter
		lv.setAdapter(adapter);
	}
}

File: Country.java

public class Country
	{
		public String name;
		public String abbreviation;
		public String region;
		public String resourceId;

		public Country()
			{
				// TODO Auto-generated constructor stub
			}

		public Country(String name, String abbreviation, String region, String resourceFilePath)
			{
				this.name = name;
				this.abbreviation = abbreviation;
				this.region= region;
				this.resourceId = resourceFilePath;
			}

		@Override
		public String toString()
			{
				return this.name;
			}
	}

File: CountryParser.java


import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import android.util.Log;

public class CountryParser {

	private static final String tag = "CountryParser";
	private static final String FILE_EXTENSION= ".png";
	
	private DocumentBuilderFactory factory;
	private DocumentBuilder builder;
	private final List<Country> list;

	public CountryParser() {
		this.list = new ArrayList<Country>();
	}

	private String getNodeValue(NamedNodeMap map, String key) {
		String nodeValue = null;
		Node node = map.getNamedItem(key);
		if (node != null) {
			nodeValue = node.getNodeValue();
		}
		return nodeValue;
	}

	public List<Country> getList() {
		return this.list;
	}

	/**
	 * Parse XML file containing body part X/Y/Description
	 * 
	 * @param inStream
	 */
	public void parse(InputStream inStream) {
		try {
			// TODO: after we must do a cache of this XML!!!!
			this.factory = DocumentBuilderFactory.newInstance();
			this.builder = this.factory.newDocumentBuilder();
			this.builder.isValidating();
			Document doc = this.builder.parse(inStream, null);

			doc.getDocumentElement().normalize();

			NodeList countryList = doc.getElementsByTagName("country");
			final int length = countryList.getLength();

			for (int i = 0; i < length; i++) {
				final NamedNodeMap attr = countryList.item(i).getAttributes();
				final String countryName = getNodeValue(attr, "name");
				final String countryAbbr = getNodeValue(attr, "abbreviation");
				final String countryRegion = getNodeValue(attr, "region");

				// Construct Country object
				Country country = new Country(countryName, countryAbbr,
						countryRegion, countryAbbr + FILE_EXTENSION);
				
				// Add to list
				this.list.add(country);
				
				Log.d(tag, country.toString());
			}
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		}
	}
}

File: CountryArrayAdapter.java


import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
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 CountryArrayAdapter extends ArrayAdapter<Country> {

	private static final String tag = "CountryArrayAdapter";
	private static final String ASSETS_DIR = "images/";
	private Context context;

	private ImageView countryIcon;
	private TextView countryName;
	private TextView countryAbbrev;
	private List<Country> countries = new ArrayList<Country>();

	public CountryArrayAdapter(Context context, int textViewResourceId,
			List<Country> objects) {
		super(context, textViewResourceId, objects);
		this.context = context;
		this.countries = objects;
	}

	public int getCount() {
		return this.countries.size();
	}

	public Country getItem(int index) {
		return this.countries.get(index);
	}

	public View getView(int position, View convertView, ViewGroup parent) {
		View row = convertView;
		if (row == null) {
			// ROW INFLATION
			Log.d(tag, "Starting XML Row Inflation ... ");
			LayoutInflater inflater = (LayoutInflater) this.getContext()
					.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
			row = inflater.inflate(R.layout.country_listitem, parent, false);
			Log.d(tag, "Successfully completed XML Row Inflation!");
		}

		// Get item
		Country country = getItem(position);
		
		// Get reference to ImageView 
		countryIcon = (ImageView) row.findViewById(R.id.country_icon);
		
		// Get reference to TextView - country_name
		countryName = (TextView) row.findViewById(R.id.country_name);
		
		// Get reference to TextView - country_abbrev
		countryAbbrev = (TextView) row.findViewById(R.id.country_abbrev);

		//Set country name
		countryName.setText(country.name);
		
		// Set country icon usign File path
		String imgFilePath = ASSETS_DIR + country.resourceId;
		try {
			Bitmap bitmap = BitmapFactory.decodeStream(this.context.getResources().getAssets()
					.open(imgFilePath));
			countryIcon.setImageBitmap(bitmap);
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		// Set country abbreviation
		countryAbbrev.setText(country.abbreviation);
		return row;
	}
}

The result should look like this:

Download:

The source code for this project can be downloaded from http://www.filefactory.com/file/b4a5548/n/IconizedListView.zip.
w2davids 2010-12-10 16:43:21.911 Will supply CountryArrayAdapter!
wizard 2010-12-10 16:39:45.044 There is no CountryArrayAdapter here so it is useless as it stands.