Friday, April 18, 2008

Custom widget adapters

In an earlier post, I wrote about the SimpleAdapter and how SimpleAdapter allows significant flexibility when laying out list items. SimpleAdapter is great but then I became curious what it takes to write an adapter.

Adapters are simple devices. On one side of the adapter is a data structure like a Java object storing data. SimpleAdapter handles Java objects that can be meaningfully translated into Strings by invoking the objects' toString() method (every Java object supports that but for quite many of them, the toString() format is not meaningful for the end user). On the other side of the adapter, there is a View that the data structure was transformed into. That View is displayed to the user. As we use Adapters to supports list views, the Adapter handles lists of Java objects (that are eventually transformed into a list of Views).

Android's built-in adapters are sufficiently versatile but it is often handy to create a custom adapter. Let's look at the following example.



You can download the example code from here.

The example is a simple weather display. A weather entry consists of 3 data items: name of the city (String), temperature in the city in degrees (integer) and an icon showing whether the sky is sunny, overcast or it is raining. The first two entries could be handled using SimpleAdapter but the third cannot: SimpleAdapter does not handle icons. Therefore we create our own adapter, called WeatherAdapter that takes list of Weather objects storing the weather info and turns that list into Views that can be rendered as list rows.

The most important part of the trick happens in a private class in WeatherAdapter.java called WeatherAdapterView. WeatherAdapter does nothing more than manages a list of WeatherAdapterViews. WeatherAdapterView is the View the Weather data object is mapped to. It is itself a composite View, composed by a LinearLayout. The LinearLayout is set up programmatically (as opposed to an XML layout) and contains two TextViews and one ImageView. The ImageView encapsulates the icon. It is worth checking how the icon images are referenced in Weather.java: as the icons are in the res/drawable directory, R.java has integer IDs for them. The getSkyResource() method in the Weather class just returns this resource ID based on the sky member variable of the Weather class.

22 comments:

spinifu said...

Hi!
Thanks very much for the code.I've used it to make a list with one more field than yours, and one icon as yours. But i'm getting this error which i cant fix up:
"List views can't have UNSPECIFIED size"
My activity is launched from other, but it never gets launched, before the layout shows up it gives me this error. I create my entries of the list dinamically, after the user presses some buttons. But i've commented the code that actually sets the customAdapter and the app won't work either way. Thanks for listening, cheers.

LukeW said...

your sample i call it a view that consist with text and static images, i mean the image is static just in the drawable folder, my question is what about the dynamic image? how to handle it with a simpleCursorAdapter or a adapter extends simpleCursorAdapter? "dynamic" here means for example the contacts icon, which can get by id by People.loadContactPhoto function. but I need a structured cursor like this: row1: id, name, phoneNumber, icon.row2: id, name, phoneNumber, icon. than what should i do to get this cursorAdapter?

zeeshan said...

can't download zip file.

Anonymous said...

Hello, do you have any idea how the yellow pressed state and the black selected state can be cutomize?

Gabor Paller said...

"Hello, do you have any idea how the yellow pressed state and the black selected state can be cutomize?"

The easiest way is to apply a theme. Sadly, the documentation is not the best, this is a very good introduction.

I also aim to provide an example, just some more days of patience, please ...

Gabor Paller said...

Here you are

andnewb said...

Hi Gobler,
I have the same problem what LukeW had asked, can you please tell us how to handle dynamic contents, how to show them in a ListView?
I want to do the keyvalue kind of mapping in which I'll fetch name and it's id from database and show only name in the list. When any click happens on name, I want to get the id of the corresponding name.

Please reply.

Gabor Paller said...

Hi, andnewb.

Are you looking for this one?

Note that the example program was created on a pre-1.0 SDK. It probably needs some massaging to run on today's SDK.

Markus said...

Hey,

I tried your code and it works fine. But I found a crazy problem. If a Button element is in the List, the list element is not more clickable. Only the button can be clicked.

Any idea how that could be?

Thanks for help and for the source code.

Gabor Paller said...

Markus, does this help?

Tavi said...

Now, that's the simplest, most to-the-point, extremely-useful piece of advice/code in the past two days and 10 Android tutorials :)

Thank you so much!

Intosia said...

Thanks! Nice tutorial, looked everywhere for some nice tut, couldnt find what i needed!

Anonymous said...

Awesome - I was looking for something very similar to this and it works perfectly. You saved me a bunch of time - thanks!

Anonymous said...

Thanks so much :)

Anonymous said...

Man! I struggled for this for three days and finally you came to rescue with a ever-so simple code! Thanks a ton!

windspinner gal said...

thanks for the sample. it was a nice thing that you shared it with the readers.

Anonymous said...

Great piece of informative code.
One question, how do you make the icons get pulled from a URL?

Kiran Wali said...

This is very good post..

Anonymous said...

thanks

Anonymous said...

thanks...

asyrofil afika fatta said...

thank you ..
your tutorials helpfull..
but the link is broken.. so i can't see your code.

Gabor Paller said...

asyrofil, I could download well the example code from the link in the post.

This is the link.