Sunday, 20 May 2012

Custom Spinner component with images

This post was inspried by a request I received via HackerBuddy, asking for help on creating a customized ListView in Android. The problem was how to show a different image on each row of the ListView. I had implemented something similar to this previously, except in my case it was a Spinner component (A Spinner is the Android equivalent of a drop-down menu selector). As the cases for ListView and Spinner are virtually the same, let's take a look at the Spinner.

Let's say we have an array of countries which we want to put into a spinner. A simple spinner containing text strings only can be created using the default ArrayAdapter provided by the Android SDK:

String[] countries = {...}; Spinner countrySpinner = new Spinner(context); ArrayAdapter adapter = new ArrayAdapter(context, android.R.layout.simple_spinner_item, countries); countrySpinner.setAdapter(adapter);

Now lets say we want have a flag icon representing each country, and we want to show the respective flag next to each country in the list. Because we now need two peices of information per country (the name and the flag), we first replace the "countries" string array with an array of Country objects. Next, we need a more complicated ArrayAdapter to deal with these Country objects, so let's extend ArrayAdapter to create a new CountrySpinnerArrayAdapter.

Country[] countries = {...}; Spinner countrySpinner = new Spinner(context); ArrayAdapter adapter = new CountrySpinnerArrayAdapter(context, android.R.layout.simple_spinner_item, countries); countrySpinner.setAdapter(adapter); class CountrySpinnerArrayAdapter extends ArrayAdapter { private List countries; public CountrySpinnerArrayAdapter(Context context, int resourceId, List countries) { super(context, resourceId, countries); this.countries = countries; } public View getView(int position, View convertView, ViewGroup parent) { TextView textView = (TextView) super.getView(position, convertView, parent); Country country = countries.get(position); textView.setText(country.getName()); textView.setCompoundDrawablesWithIntrinsicBounds(country.getFlagImage(), null, null, null); return textView; } public View getDropDownView(int position, View convertView, ViewGroup parent) { return getView(position, convertView, parent); } }

What's happening here is the adapter returns the required View for each item in the list. This View can be modified to meet our requirements, which in the case means setting an image to the left hand side of the text, using the textView.setCompoundDrawablesWithIntrinsicBounds(...) method.

To see this implemented in the wild, check out my Daylight World Map app.