A Dynamic Presentation
This technique — supplying an alternate layout to use for rows — handles simple cases very nicely. However, it isn’t sufficient when you have more-complicated scenarios for your rows, such as the following:
• Not every row uses the same layout (e.g., some have one line of text, others have two).
• You need to configure the widgets in the rows (e.g., different icons for different cases).
In those cases, the better option is to create your own subclass of your desired Adapter
, override getView()
, and construct your rows yourself. The getView()
method is responsible for returning a View
, representing the row for the supplied position in the adapter data.
For example, let’s rework the preceding code to use getView()
so we can have different icons for different rows — in this case, one icon for short words and one for long words (from the FancyLists/Dynamic
sample project at http://apress.com/):
public class DynamicDemo 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 (this));
selection = (TextView)findViewById(R.id.selection);
}
public void onListItemClick(ListView parent, View v,
int position, long id) {
selection.setText(items[position]);
}
class IconicAdapter extends ArrayAdapter {
Activity context;
IconicAdapter(Activity context) {
super(context, R.layout.row, items);
this.context = context;
}
public View getView(int position, View convertView,
ViewGroup parent) {
LayoutInflater inflater = context.getLayoutInflater();
View row = inflater.inflate (R.layout.row, null);
TextView label = (TextView)row.findViewById(R.id.label);
label.setText(items[position]);
if (items[position].length() > 4) {
ImageView icon = (ImageView)row.findViewById(R.id.icon);
icon.setImageResource(R.drawable.delete);
}
return(row);
}
}
}
The theory is that we override getView()
and return rows based on which object is being displayed, where the object is indicated by a position index into the Adapter
. However, if you look at the implementation shown in the code here, you will see a reference to a LayoutInflater
class — and that concept takes a little bit of an explanation.
A Bit About Inflation
In this case, “inflation” means the act of converting an XML layout specification into the actual tree of View
objects the XML represents. This is undoubtedly a tedious bit of code: take an element, create an instance of the specified View
class, walk the attributes, convert those into property setter calls, iterate over all child elements, lather, rinse, repeat.
The good news is that the fine folk on the Android team wrapped all that up into a class called LayoutInflater
that we can use ourselves. When it comes to fancy lists, for example, we will want to inflate Views
for each row shown in the list, so we can use the convenient shorthand of the XML layout to describe what the rows are supposed to look like.
In the preceding example, we inflate the R.layout.row
layout we created in the previous section. This gives us a View
object that, in reality, is our LinearLayout
with an ImageView
and a TextView
, just as R.layout.row
specifies. However, rather than having to create all those objects ourselves and wire them together, the XML and LayoutInflater
handle the “heavy lifting” for us.
And Now, Back to Our Story
So we have used LayoutInflater
to get a View
representing the row. This row is “empty” since the static layout file has no idea what actual data goes into the row. It is our job to customize