Sunday, March 16, 2008

My first meeting with the SimpleAdapter widget

Such a shame. Normally, Android developers have their SimpleAdapter adventures right at the beginning of their Android development career, meanwhile I met this beast just now. I wouldn't say that this UI feature is overdocumented and that there are truckloads of example programs out there (if there are, Google does not find them) so I decided to share my experiences with whoever cares to read this blog entry.

It all started with a very simple wish that I wanted to create a list like this:


Each list entry has two lines and the lines are styled differently. Fortunately, there is an example program at the ListActivity documentation page which happens not to work and takes the input behind the list from a database. So it took me some time to find my friend, the SimpleAdapter widget and to find out, how to use it.

You can download the example program from here.

The root of the problem is that each element of this list is a complex view, consisting of two TextViews (XML mangling because of blog engine limitations):

[?xml version="1.0" encoding="utf-8"?]
[LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"]

[TextView android:id="@+id/text1"
android:textSize="16px"
android:textStyle="bold"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/]

[TextView android:id="@+id/text2"
android:textSize="12px"
android:textStyle="italic"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/]
[/LinearLayout]

The corresponding SimpleAdapter maps an element from list of objects to this view.

SimpleAdapter notes = new SimpleAdapter(
this,
list,
R.layout.main_item_two_line_row,
new String[] { "line1","line2" },
new int[] { R.id.text1, R.id.text2 } );

Now this is not a trivial line of code. What it says is:
  • "list" is a reference to an object implementing the List interface. Each element in this list is an object implementing the Map interface. In our implementation example, the list is ArrayList and the elements in this list are HashMaps.
  • The layout describing one row in the main list is defined in layout/main_item_two_line_row.xml file.
  • In each individual HashMap, there are two key-value pairs. The keys are "line1" and "line2", the corresponding values are arbitrary objects whose toString() method yields the value displayed in the list row. In our case, the values are Strings.
  • Values stored with key "line1" will be displayed in the TextView whose id is "text1". Similarly, "line2" Map key is associated with "text2" TextView.
You can try the program following the instructions in the Start here entry. You can add items to the list using the menu.

37 comments:

Anonymous said...

Thanks that was a really interesting example. I've been trying to get multi row, multi column lists working for a while, and you're right it’s not over documented. The one google example is not compileable.

Your code did not work out of the box for me. In the main_item_two_line_row.xml file I had to strip android: from before the keyword id to get it to compile.

TextView android:id="@+id/text1" changed to TextView id="@+id/text1" and in two other places.

That brought it in line with google’s simple list examples.

Now any advice on getting a single row, two column list to work?

Cheers, Francis

Gabor Paller said...

Do you use the latest emulator (m5-rc14)? If you don't, migrate as soon as possible. They have changed the schema and simple "id" does not work anymore.

IsCyborg said...

Oh my god I'm retarded. I had it right, but what I was forgetting to do was add the new HashMap[String,String]s I was creating to the ArrayList! That would probably explain why no values were showing up.

Anyways; excellent article, thanks for jogging my brain.

Gábor Török said...

Hi Gabor,

Thanks for the useful code snippet, it helped me to understand how Listboxes work on Android, too.

However, in order for the code to work on the latest SDK (1.0 R2) it required some patches:
- In onCreateOptionsMenu method menu.add(...) had to be introduced by another argument like this:
menu.add(0, ADD_ITEM_ID, 0, R.string.add_item );

- onOptionsItemSelected didn't work as is, either, and I found that you'd probably use onMenuItemSelected instead. And its first two lines would look like this:
public boolean onMenuItemSelected(int featureId, MenuItem item) {
switch ( item.getItemId() ) {

You might wish to adjust your code according to these findings or not ... it's really up to. :)

Thanks again!

Anonymous said...

This example is very useful. I am just starting to dabble in Android and I encountered the same problem with the ListView. I wanted to have a multi-row,multi-colum list view and this blog just solved it for me.

Excellent article!

Unknown said...

You don't need to instantiate a new notes object for each new item.

Here's my modified addItem method:

private void addItem() {
long ts = System.currentTimeMillis();
int lastDigit = (int)( ts % 10 );
HashMap item = new HashMap();
item.put( "line1",Long.toString( ts ) );
item.put( "line2","lastDigit: "+Integer.toString( lastDigit ) );
list.add( item );
notes.notifyDataSetChanged();
}

Keep onCreate the same and make notes a class variable. I think this is closer to the intent of the adapter.

Gabor Paller said...

Thanks, Mark, you are right. I updated the sample program.

It was just about time, this sample program from 2008 March did not compile with the standard SDK. :-)

Anonymous said...

hi, i have downloaded this code and when i ran this code on my machine it says no items found could you please help how to add items??
i am new to android
and i want to make it click able to show details of records..

Gabor Paller said...

Anonymous: Menu/Add item :-)

Anonymous said...

sorry i did not understand could you please tell me in detail i am new to android....

Anonymous said...

ok friend got it...

on clicking menu then add item appears LOL..

thanks..

Anonymous said...

i have one more question

if i try to run additem() function on oncreate function

it does not do anything..
for testing i want to add two or three items on loading activity
what you suggest what should i do???

Gabor Paller said...

Try this:

Test-driving the Android GUI

myname here said...
This comment has been removed by the author.
myname here said...

nevermind. erase previous post and this one. thank you.

d said...

thanks a lot!!!!!!!!!!
This tutorial has saved my lot of time.
Thanks really.

Anonymous said...

hi,
nice tutorial!! saved me lot of time! thx!
but i have one more question: how to pass some attributes to each row? such as bgColor or height?
thx!
tom

Gabor Paller said...

Anonymous, check this post. Be careful, that example program was written for pre-1.0 SDK, it needs some updating. The instructions are in the comments of the post.

Anonymous said...

Great tutorial, thanks. I was wondering if you have tried the android HelloSpinner, it uses an arrayAdapter, and I have a heck of a time adding items into the arrayAdapter. The adapter.Add method does not seem to work based on all the posts out there....your input will be appreciated

Gabor Paller said...

Anonymous, many Android adapters have a trick that they don't refresh the widget tree (i.e. the visible elements) until they are instructed to do so. The magic method is notifyDataSetChanged() in android.widget.BaseAdapter (parent class for ArrayAdapter, e.g.) which conveniently has no documentation text in the Android reference (just the method headline). For ArrayAdapter, you also have setNotifyOnChange(). If you invoke when you set up your adapter, notifyDataSetChanged() will be invoked automatically when you add, delete or modify an element. If your widget that the adapter is attached to is already visible and you add a large amount of items then I don't recommend setNotifyOnChange( true ) as it causes visible flicker on screen.

This is a less documented part of adapters, I admit.

Anonymous said...

Thanks for the advice; actually it dies at the Add method, so I could not even get to the notifyDataSetChanged() that you mentioned. I plan to switch over using the database method instead of using the resource file option - HT

Gabor Paller said...

Anonymous, is multithreading involved somehow? Android widgets including adapters can only be manipulated from the app's ui thread. There are tricky ways to get multithreading into the picture, e.g. rpc.

Anonymous said...

Thank you, You saved my time.

Anonymous said...

This was a useful tutorial on SimpleAdapter.
SimpleAdapter was not as simple as I thought.

Khmer Network said...

Thank for your sample code, it also need to use two line item for listview. Can u show me how to get value from selected item with your code? i can get selected value from a single row item listview. but with your code i can't. can u show me ?

Khmer Network said...

thank, now i can get value from two line item with your code.

String str;
str=list.get(position).toString();

//list.remove(position); //notes.notifyDataSetChanged();
Toast.makeText(getApplicationContext(), str, Toast.LENGTH_SHORT).show();

Manish Kungwani said...

Thanx .. that really helped and saved a lot of effort of going through the not-working overly complex tutorials. :)

Mohamed Elkammar said...

Thanks a lot for the great article.. very helpful and explanatory to beginners. Official documentation continue to strike me as useless in most cases.

I hope you don't mind me referencing your article in my blog.

Gabor Paller said...

Mohammed, of course I don't mind, thanks!

ishtek said...

Thanks....

Lachlan said...

THANK YOU VERY VERY MUCH! I wasted 6 hours coding trying to get my list to work, finally I saw your information and got it working.

I had a small problem with notes.notifyDataSetChanged(); - it didn't seem to post the information to the list from a loop, but got around that pretty quickly.

Thank you again!

Bang said...

Thanks. I'm reading an article similar to this which links to your article. So I thought it is more convenient if I can read another article on this topic.

Thanks for helping us. This solved my issue.

Junaid Ullah Nadeem said...

Thanks a lot...
it helped alot... Thanking u again...

tizen vs android said...

Hi friend, Your blogs are very cooperative and informative. pleasing information benefiting item. This mail is actually the best on this prized topic. "I actually relish to read all the content is dispatched on your blog. It's very simple to read, the content is large, and you’re an educated scribe different most of the blogs. thanks for the post.

Gabor Paller said...

tizen vs. android, may I recommend this forum?

Your Tizen-related comments would be welcome there.

Unknown said...

AWESOME!!!!!!!!!!

Unknown said...

If you have a problem with notifyDataSetChanged(), just delete "SimpleAdapter"
in
SimpleAdapter notes = new SimpleAdapter
new line
notes = new SimpleAdapter
This is because we have created a new variable in the class
private SimpleAdapter countries;
And if we create it again in oncreate method, then
we will get an error
Attempt to invoke virtual method 'void android.widget.SimpleAdapter.notifyDataSetChanged()' on a null object reference