ListView

ListView – Part 3

In the previous article of this series we got a simple ListView working with some click handling. In this article we are going to use a custom layout which includes an ImageView and a TextView instead of one of the stock Android layouts.

OK, let’s begin by adding a new layout which will be used to layout each item. We’l create this in res/layout/item.xml:

[xml]




[/xml]

All we are really doing here is taking the TextView that comprises the stack layout for android.R.layout.simple_list_item_1, wrapping it in a horizontal LinearLayout, adding an ImageView, and giving the TextView an android:layout_weight of 1 to stretch to fill the available space. We also give the LinearLayout an android:minHeight of “?android:attr/listPreferredItemHeight” so that we maintain consistency of the height of each ListView item. Additionally, we make the ImageView item clickable.

The next thing that we need to do is to add a custom ListAdapter to make use of this layout. In many cases we can get away with using SimpleAdapter as we did in the previous example, and simple add mappings to the constructor. However, in this example, we want to make the ImageView clickable, and we need to add listener to handle click events which cannot be done using SimpleAdapter, hence we need a custom adapter.

This isn’t difficult. All we need to do is to create a new class which extends BaseAdapter, override the required methods, and add a constructor to initialise the adapter. The adapter that we’ll create will use an internal List of String objects to store the text that will be bound to the TextView control.

Before we proceed, I must give a warning: While the following adapter implementation works, it is very inefficient. There are mechanisms built in to Android to make this far more efficient which we will cover in due course. In the meantime I would strongly advise against using the following adapter in production code. Use the examples which follow instead.

Warning out of the way, here’s our implementation of CustomAdapter:

[java] public class CustomAdapter extends BaseAdapter
{
private final Context context;
private final List items;

public CustomAdapter( Context context, List items )
{
this.context = context;
this.items = items;
}

@Override
public int getCount()
{
return items.size();
}

@Override
public Object getItem( int position )
{
return items.get( position );
}

@Override
public long getItemId( int position )
{
return getItem( position ).hashCode();
}

@Override
@Override
public View getView( int position, View convertView, ViewGroup parent )
{
/*
* Please note that while this code works it is somewhat inefficient
* and may result in some jerky scrolling. Please read the article
* which explains this code at http://blog.stylingandroid.com/archives/623
* for further explanation and base any production code on the later,
* more efficient examples.
*/
LayoutInflater inflater = (LayoutInflater)
context.getSystemService( Context.LAYOUT_INFLATER_SERVICE );
View v = inflater.inflate( R.layout.item, parent, false );
final String item = (String) getItem( position );
TextView tv = (TextView)v.findViewById( android.R.id.text1 );
ImageView iv = (ImageView)v.findViewById( R.id.imageView );
tv.setText( item );
iv.setOnClickListener( new OnClickListener()
{

@Override
public void onClick( View v )
{
Toast.makeText( context,
String.format( “Image clicked: %s”, item ),
Toast.LENGTH_SHORT ).show();
}
} );
return v;
}
}
[/java]

The constructor take a Context instance (which we’ll need later), and the data that we’ll bind to – a List of String items. It stores these to instance variables. The getCount() and getItem() methods should both be self-explanatory – they return the number of items and a specific item respectively. The getItemId() method returns a unique ID for each item (this can be useful if we want to identify the same item before and after updating the list data and it’s actual position within the list has changed) and we simply return the hashCode of the relevant item. Finally the getView() method is the real work horse – it generates a data-bound view for each item.

First we get a LayoutInflator instance from the Context and inflate R.layout.item into a View hierarchy. next we look up the data item, and the TextView and ImageView controls from our View hierarchy. We then set the text on the TextView, and finally set an OnClickListener on the ImageView. Note that we have changed the text of the toast that we generate so that we can differentiate between a click on the item itself and one on the ImageView.

Now we need to modify the onCreate() method of ListViewActivity to use our new adapter:

[java] @Override
public void onCreate( Bundle savedInstanceState )
{
super.onCreate( savedInstanceState );
setContentView( R.layout.main );

List data = new ArrayList();
for ( int i = 1; i <= 10; i++ ) { data.add( String.format( "Item %d", i ) ); } CustomAdapter adapter = new CustomAdapter( this, data ); ListView listView = (ListView)findViewById( android.R.id.list ); listView.setAdapter( adapter ); listView.setOnItemClickListener( new OnItemClickListener() { @Override public void onItemClick( AdapterView listView, View view,
int pos, long id )
{
TextView textView =
(TextView) view.findViewById( android.R.id.text1 );
toast( (String) textView.getText() );
}
} );
}
[/java]

This is actually simpler than the previous implementation because we have simplified the data model that we pass to the adapter. If we run this, everything works and we correctly handle the click events on both the item, and when the user specifically clicks on the ImageView:

Item Click
Item Click
ImageView click
ImageView click

As I mentioned before, this works but is inefficient and can result in some poor performance when scrolling, particularly on older devices. In the next part of this series we’ll look at how we can optimise things.

The source code for this article can be found here.

© 2011, Mark Allison. All rights reserved.

Copyright © 2011 Styling Android. All Rights Reserved.
Information about how to reuse or republish this work may be available at http://blog.stylingandroid.com/license-information.

4 Comments

  1. Very good post, Mark! I’ll be in Brazil AndroidConf, in Rio de Janeiro, you will be there, right? I hope to meet you!

    Great content and great blog, helped me a lot, congratulations!

    1. Thanks for the kind comments, and yes, I’ll be there speaking about Android Layouts – I’m really looking forward to it. Please come and say “Hi!”

      1. Absolutely! It’s great to see more and more people working with Android, and transmits the knowledge to other people! I work with ui design and iconography and their posts were very helpful to me, really.

        So, see you in AndroidConf and I’ll say hi! đŸ˜€

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.