Return of the Fancy ListViews

BuildingDroidsLong-time followers of this column may recall a blog post series I did back in the summer of 2008, covering “fancy ListViews“. This referred to being able to take control over what rows look like in a ListView, so you can tailor each to appear exactly as you want. Today, let’s revisit the topic, in light of some changes in recent Android SDK releases.

You may recall that one of the techniques those earlier posts covered was the use of a “holder” or “wrapper”. This cached the results of findViewById() lookups in a custom class, an instance of which was stored in each row View’s “tag” via setTag(). When a row is recycled, you can grab the wrapper via getTag(), then use the wrapper to access the individual widgets inside of the row (e.g., ImageView, TextView). On a list that may be extensively scrolled, this saves hundreds, if not thousands, of findViewById() calls, which are not exactly cheap.

Android 1.6 quietly added a pair of new methods to the View class which can simplify the creation of fancy lists like the ones shown in this chapter. Specifically, there are two new versions of getTag() and setTag() that take an identifier along with their object. These let you eliminate the wrapper class while still reaping much of its benefits.

Below, you will see some code from a sample project that uses these new getTag()/setTag() methods to cache findViewById() lookups:

[java]
package com.commonsware.android.fancylists.eight;

import android.app.Activity;
import android.os.Bundle;
import android.app.ListActivity;
import android.view.View;
import android.view.ViewGroup;
import android.view.LayoutInflater;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

public class SelfWrapperDemo extends ListActivity {
TextView selection;
String[] items={"lorem", "ipsum", "dolor", "sit", "amet",
"consectetuer", "adipiscing", "elit", "morbi", "vel",
"ligula", "vitae", "arcu", "aliquet", "mollis",
"etiam", "vel", "erat", "placerat", "ante",
"porttitor", "sodales", "pellentesque", "augue",
"purus"};

@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
setListAdapter(new IconicAdapter());
selection=(TextView)findViewById(R.id.selection);
}

private String getModel(int position) {
return(((IconicAdapter)getListAdapter()).getItem(position));
}

public void onListItemClick(ListView parent, View v,
int position, long id) {
selection.setText(getModel(position));
}

class IconicAdapter extends ArrayAdapter<String> {
IconicAdapter() {
super(SelfWrapperDemo.this, R.layout.row, items);
}

public View getView(int position, View convertView,
ViewGroup parent) {
View row=convertView;
if (row==null) {
LayoutInflater inflater=getLayoutInflater();
row=inflater.inflate(R.layout.row, parent, false);
row.setTag(R.id.label, row.findViewById(R.id.label));
row.setTag(R.id.icon, row.findViewById(R.id.icon));
}

TextView label=(TextView)row.getTag(R.id.label);
ImageView icon=(ImageView)row.getTag(R.id.icon);
label.setText(getModel(position));

if (getModel(position).length()>4) {
icon.setImageResource(R.drawable.delete);
}
else {
icon.setImageResource(R.drawable.ok);
}
return(row);
}
}
}
[/java]

When we inflate our row, we also pull out all widgets we might need to modify at runtime (R.id.label and R.id.icon) and stash those widgets as “tags” under their respective IDs. Then, regardless of row source, we pull out the widgets from those tags and update them. We gain the widget-level caching that a wrapper would provide us, but we do not need an actual wrapper class.

There are three caveats:

  • This technique does not offer any of the other potential benefits of a wrapper class, such as lazy-fetching
  • The index provided to getTag() and setTag() must be an identifier — you cannot just use 0, 1, etc. like you might in an ArrayList
  • This only works on Android 1.6…though there are tricks to get this same sort of logic working on Android 1.5 via reflection, a topic for another time

NOTE: this blog post is derived from a section in The Busy Coder’s Guide to Android Development, Version 2.8. A free excerpt from this book, covering all of the “fancy ListView” topics, can be downloaded as a PDF from the CommonsWare site.

About author

AndroidGuys
AndroidGuys 4641 posts

Founded on November 5, 2007, we've enjoyed bringing you the latest in Android news and rumors. Updated daily, we strive to deliver reviews, opinions, and updates on all things related to Android.

You might also like

News and Rumors

Evernote optimizes app with Android 4.1-specific features

Evernote recently debuted a new version of its popular, and increasingly cooler note-taking/utility tool application with features specifically created for Android 4.1 Jelly Bean.  This release (v4.2) gives users an even

News and Rumors

Meizu MX Confirmed for China, Quad-Core in Tow

Yes, you read that right. We said quad-core. The Meizu MX has been made official for China, and despite the somewhat unknown name, the device looks pretty solid. It’s packing

News and Rumors

Beats Music subscription service goes live

AT&T customers can take advantage of  multi-listener family plan Today sees the introduction of Beats Music, the subscription service with curated and personalized music with a library of more than

17 Comments

  1. Brandon
    November 09, 21:40 Reply

    when you do/display tutorials there should be an image of the final product. It's nice to see what I'm getting when following a tutorial.

    • commonsguy
      November 09, 22:22 Reply

      This isn't strictly a tutorial, more of a code sample illustrating a point.

  2. Romain Guy
    November 09, 22:23 Reply

    The new "tags" API is not meant to be used this way and queried often. It may (potentially) be expensive to use. You should stick to the view holder pattern (and, if I may be annoying, please refer to it as the view holder pattern, and not "wrapper," since it's not a wrapper at all and that's not how our examples call it.)

    • commonsguy
      November 09, 22:27 Reply

      Uh, OK. What the heck is it there for, if not this? Considering the key has to be an ID, and the limited set of use cases for View tags in the first place, this seemed like the logical explanation.

      • Dianne
        November 10, 12:24 Reply

        This was added to allow different entities to associate their own tags with the view, without trampling on each other. The specific case this is addressing is some places in the framework where we want to associate a tag with a view, but that would conflict with an application if it also wanted to set a tag. So applications should continue to use the single fast tag; the framework (and other libraries that will be used by applications) should use tags with identifiers.

        • commonsguy
          November 10, 17:40 Reply

          Ah. That makes sense, though tying keys to R. values seems to be an odd restriction in this case. Many "other libraries" won't have R. values of their own, because, well, they're libraries and therefore don't have resources. I can see how you were aiming for it to be a unique value that way, and I'm not sure how else you could do it, but it does mean I can't use the new getTag()/setTag() in a reusable JAR unless I require a unique ID to be given to my by the application via a method on some object, which may or may not be convenient for me or the application.

          All that being said, I understand now the role the new getTag()/setTag() are supposed to play. Thanks for the info!

  3. Bill
    November 10, 00:37 Reply

    "…can be downloaded as a PDF from the CommonsWare site."

    Follow the link and I get: 403 Forbidden

    –Bill

  4. kwo
    February 16, 19:25 Reply

    The <code> tags in this post are being displayed as code snippets, not inline, and are therefore making this post all but unreadable.

  5. Marapes
    April 06, 11:19 Reply

    Yes, I am afraid these posts are not friendly user at all, their style is horrible :-/

  6. Marapes
    April 06, 11:21 Reply

    I mean their CSS styling, not the post's style. The same text with a good css layout and some pics would be simply great.

Leave a Reply