Well what do they have in common ?……purely coincidence! I recently worked on a project that required a search functionality. The search however involved different categorized items.

I was thinking of a neat visual way to quickly distinguish between results from different categories. Totally COINCIDENTLY at that exact moment a colleague shouted …”sharks with lasers on their heads!”

Download : IconizedListView.zip

Some Notes:

  1. XML Parsing
  2. ArrayAdapter for modifying visual display of list items

File: listview.xml

<?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: raw/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;
	}
}