Customizing Android ListView Item Layout
Today I'm going to go step by step through an example of using a ListView, and customizing the visual layout of items within. ListView is one of the primary UI components in the Android SDK. Its a great tool, providing you with scrolling lists and selection functionality right out of the box. With very little code, you can have a ListView displaying a list of objects for you. Just toss something like
<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent">
<listview android:id="@+id/ListViewId" android:layout_width="fill_parent" android:layout_height="wrap_content" />
</linearlayout>
in your layout file (e.g. main.xml, for a simple application), and something like
setContentView(R.layout.main);
String[] items = {"red", "blue","green"};
ListView listView = (ListView) findViewById(R.id.ListViewId);
listView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, items));
on the java side in your onCreate() method. Easy, huh? Running this application will produce the following:
<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content" android:gravity="left|center" android:layout_width="wrap_content" android:paddingbottom="5px" android:paddingtop="5px" android:paddingleft="5px">
<imageview android:id="@+id/avatar" android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_marginright="6dip" android:src="@drawable/icon" />
<linearlayout android:orientation="vertical" android:layout_width="0dip" android:layout_weight="1" android:layout_height="fill_parent">
<TextView android:id="@+id/username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"/>
<textview android:id="@+id/email" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginleft="10px" android:textcolor="#0099CC" />
</linearlayout>
</linearlayout>
This may look a bit complicated, but its really not. This xml will just define the layout of our items, so each selection in the ListView will have this layout within it. We've created a linear layout as our root layout (note: since we didn't specify, this layout will have a horizontal orientation for components inside of it). Inside of this base structure, we have two sub-components: an ImageView, which will display a single image, and a LinearLayout components with a vertical orientation. In this inner LinearLayout, we have two TextView components. This will display two text fields, one on top of the other, horizontally adjacent to the ImageView. Take a look at the ImageView definition. We have to tell the ImageView what image it will display. We tell it this with the android:src parameter. For simplicity, we are giving this a value of "@drawable/icon", which will tell it to use a file named icon.* (any image extension) found in one of the "res/drawable-*" directories. This icon file is included in any new android project created through eclipse, making it an easy target - you could just as easily point it at any image file you chose to put in these directories. In a real application to display user avatars, we would want to change this value in our java code to place a different avatar image in each ListView item. Now we just have to create a new Adapter class that will tell our ListView how to user this new item layout. To make things easier, I'll first create a small UserRecord class to allow us to pass our user item data around easily. Here it is:
public class UserRecord {
public String username;
public String email;
public UserRecord(String username, String email) {
this.username = username;
this.email = email;
}
}
Now for the adapter, which is a bit more complex:
public class UserItemAdapter extends ArrayAdapter<UserRecord> {
private ArrayList<UserRecord> users;
public UserItemAdapter(Context context, int textViewResourceId, ArrayList<UserRecord> users) {
super(context, textViewResourceId, users);
this.users = users;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.listitem, null);
}
UserRecord user = users.get(position);
if (user != null) {
TextView username = (TextView) v.findViewById(R.id.username);
TextView email = (TextView) v.findViewById(R.id.email);
if (username != null) {
username.setText(user.username);
}
if(email != null) {
email.setText("Email: " + user.email );
}
}
return v;
}
}
So what are we doing here? Let's take a look. We are creating an adapter class which extends ArrayAdapter
- int position - the position of the item in the array we want to display
- a View - the view that will display the item in question
- a ViewGroup parent - the group our view belongs to. We won't need to deal with this for the task at hand. We do a little handling at the top in case the View object passed in is null (it shouldn't be in this example), and then we can set some values. Since we grabbed the list of UserRecord objects and stored it in a member variable in the UserItemAdapter constructor, we just get the objects at the specified position to have access to all of the values we want (just username and email, here, but obviously it could be much more data). Using a few findViewById() calls on our View, we get the UI components (TextView objects, in this case) that we want to use to displat our data, and set their text values using the specified UserRecord object. Return the View, and viola! Our ListView now looks like:
Get 50% off my Node.js course here
If you liked this article, help me out by sharing a 50% discount to my Node.js course here: Tweet Thanks!
You should follow me on Twitter here: Follow @AaronCois