Android - Create a custom multi-line ListView bound to an ArrayList

The Android HelloListView tutorial shows how to bind a ListView to an array of string objects, but you'll probably outgrow that pretty quickly. This post will show you how to bind the ListView to an ArrayList of custom objects, as well as create a multi-line ListView.

Let's say you have some sort of search functionality that returns a list of people, along with addresses and phone numbers. We're going to display that data in three formatted lines for each result, and make it clickable.

First, create your new Android project, and create two layout files. Main.xml will probably already be created by default, so paste this in:

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android=""  android:orientation="vertical"  android:layout_width="fill_parent"   android:layout_height="fill_parent">  <TextView   android:layout_height="wrap_content"   android:text="Custom ListView Contents"   android:gravity="center_vertical|center_horizontal"   android:layout_width="fill_parent" />   <ListView    android:id="@+id/ListView01"    android:layout_height="wrap_content"    android:layout_width="fill_parent"/> </LinearLayout>

Next, create a layout file called custom_row_view.xml. This layout will be the template for each individual row in the ListView. You can use pretty much any type of layout - Relative, Table, etc., but for this we'll just use Linear:

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android=""  android:orientation="vertical"  android:layout_width="fill_parent"   android:layout_height="fill_parent">   <TextView android:id="@+id/name"   android:textSize="14sp"   android:textStyle="bold"   android:textColor="#FFFF00"   android:layout_width="wrap_content"   android:layout_height="wrap_content"/>  <TextView android:id="@+id/cityState"   android:layout_width="wrap_content"   android:layout_height="wrap_content"/>  <TextView android:id="@+id/phone"   android:layout_width="wrap_content"   android:layout_height="wrap_content"/> </LinearLayout>

Now, add an object called SearchResults. Paste this code in:

public class SearchResults {  private String name = "";  private String cityState = "";  private String phone = "";

 public void setName(String name) {   this.name = name;  }

 public String getName {   return name;  }

 public void setCityState(String cityState) {   this.cityState = cityState;  }

 public String getCityState {   return cityState;  }

 public void setPhone(String phone) {   this.phone = phone;  }

 public String getPhone {   return phone;  } }

This is the class that we'll be filling with our data, and loading into an ArrayList.

Next, you'll need a custom adapter. This one just extends the BaseAdapter, but you could extend the ArrayAdapter if you prefer.

public class MyCustomBaseAdapter extends BaseAdapter {  private static ArrayList<SearchResults> searchArrayList;    private LayoutInflater mInflater;

 public MyCustomBaseAdapter(Context context, ArrayList<SearchResults> results) {   searchArrayList = results;   mInflater = LayoutInflater.from(context);  }

 public int getCount {   return searchArrayList.size;  }

 public Object getItem(int position) {   return searchArrayList.get(position);  }

 public long getItemId(int position) {   return position;  }

 public View getView(int position, View convertView, ViewGroup parent) {   ViewHolder holder;   if (convertView == null) {    convertView = mInflater.inflate(R.layout.custom_row_view, null);    holder = new ViewHolder;    holder.txtName = (TextView) convertView.findViewById(R.id.name);    holder.txtCityState = (TextView) convertView.findViewById(R.id.cityState);    holder.txtPhone = (TextView) convertView.findViewById(R.id.phone);

   convertView.setTag(holder);   } else {    holder = (ViewHolder) convertView.getTag;   }      holder.txtName.setText(searchArrayList.get(position).getName);   holder.txtCityState.setText(searchArrayList.get(position).getCityState);   holder.txtPhone.setText(searchArrayList.get(position).getPhone);

  return convertView;  }

 static class ViewHolder {   TextView txtName;   TextView txtCityState;   TextView txtPhone;  } }

(This is basically the same as the List14.java API demo)

Finally, we'll wire it all up in the main class file:

public class CustomListView extends Activity {     @Override     public void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.main);                 ArrayList<SearchResults> searchResults = GetSearchResults;                 final ListView lv1 = (ListView) findViewById(R.id.ListView01);         lv1.setAdapter(new MyCustomBaseAdapter(this, searchResults));                 lv1.setOnItemClickListener(new OnItemClickListener {          @Override          public void onItemClick(AdapterView<?> a, View v, int position, long id) {           Object o = lv1.getItemAtPosition(position);           SearchResults fullObject = (SearchResults)o;           Toast.makeText(ListViewBlogPost.this, "You have chosen: " + " " + fullObject.getName, Toast.LENGTH_LONG).show;          }          });     }         private ArrayList<SearchResults> GetSearchResults{      ArrayList<SearchResults> results = new ArrayList<SearchResults>;            SearchResults sr1 = new SearchResults;      sr1.setName("John Smith");      sr1.setCityState("Dallas, TX");      sr1.setPhone("214-555-1234");      results.add(sr1);            sr1 = new SearchResults;      sr1.setName("Jane Doe");      sr1.setCityState("Atlanta, GA");      sr1.setPhone("469-555-2587");      results.add(sr1);            sr1 = new SearchResults;      sr1.setName("Steve Young");      sr1.setCityState("Miami, FL");      sr1.setPhone("305-555-7895");      results.add(sr1);            sr1 = new SearchResults;      sr1.setName("Fred Jones");      sr1.setCityState("Las Vegas, NV");      sr1.setPhone("612-555-8214");      results.add(sr1);            return results;     } }

Notice that we first get an ArrayList of SearchResults objects (normally this would be from an external data source...), pass it to the custom adapter, then set up a click listener. The listener gets the item that was clicked, converts it back to a SearchResults object, and does whatever it needs to do.

Fire it up in the emulator, and you should wind up with something like this:

!(/images/bosuch/android---create-a-custom-multi-line-listview-bound-to-an/ec4ff095-r_CustomListView.gif)

Update (2/22/2012): I've uploaded a complete Eclipse project of this tutorial to Dropbox:

Technorati Tags:

This article is part of the GWB Archives. Original Author: Bill Osuch

New on Geeks with Blogs

  • We Won The One Award I Actually Care About

    Full Scale made the Inc. 5000 for the fifth year straight, the 12th listing across my three companies. Here is why the one award you cannot buy is worth stopping for.

  • Your Customers Build the Features Now

    I let a tool I liked sit dead for a year rather than build the features I wanted. An MCP server meant I never had to, and your customers can do the same to your product.

  • Get the Size of a Directory in Linux the Easy Way

    du -sh for the quick answer, ncdu for the cleanup, df for the disk itself: every command for checking directory size in Linux, plus why du and df never agree.

  • Vim Search and Replace: The Ultimate Guide

    One :%s command replaces every match in a file before a find dialog would even open. The Vim substitute patterns worth the muscle memory: flags, ranges, capture groups, and multi-file edits.