The notes taken out of the Google I/O talk “The world of ListView“.
Due to performance and memory considerations, the ListView views hierarchy is fixed, nothing that is not displayed on screen is not part of the hierarchy. The item views are recycled.
Terminology
- index (refers to the child view),
- position (refers to the Data in Adapter),
- id (refers to the unique identifier for data).
Stable IDs:
- if hasStableIds() == true, the ListView can do a number of tricks to presentation and handling of the items.
- An ID always refers to the same value.
- Helps ListView.
Adapters
- getView(int position, View convertView, ViewGroup parent)
- full data presentation control
- Potential optimisation through the re-use of convertView
getView()
the slow way
public View getView(int position, View convertView, ViewGroup parent) {
View item = mInflater.inflate(R.layout.blabla, null);
((TextView) item.findViewById(R.id.text)).setText(DATA[position]);
((ImageView) item.findViewById(R.id.icon)).setImageBitmap(
(position &1) == 1 ? mIcon1 : mIcon2);
return item;
}
the correct way
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.blabla, parent, false);
}
((TextView) convertView.findViewById(R.id.text)).setText(DATA[position]);
((ImageView) convertView.findViewById(R.id.icon)).setImageBitmap(
(position &1) == 1 ? mIcon1 : mIcon2);
return convertView;
}
the Fast Way
create a special datastructure in your app, eg.
static class ViewHolder {
TextView text;
ImageIcon icon;
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(...
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.text);
holder.icon = (ImageView) convertView.findViewById(R.id.icon);
convertView.setTag(holder);
} else {
holder = (viewHolder) convertView.getTag();
}
holder.text.setText(DATA[position]);
holder.icon.setImageBitmap(...
return convertView;
}
Comments
- do not change convertView structure (use view type system for this instead)
- handling data changes: use the adapter properly, then notifyDataSetChanged() (for new or updated data); notifyDataSetInvalidated() (in case there is no more data available).
Built-in view type system
- getItemViewType, type of view for a given position. Used to provide the right convertView
- getViewTypeCount (how many different types to expect)
Slow data source
- Adapter modifications on the UI thread
- Fetching data can happen enywhere, request data on another thread
- Commit adapter changes on the UI thread
- Call notifyDataSetChanged() in the same UI thread.
Item properties
- Enabled: item can be selected, clicked
- Disabled: section dividers/headers within content
- Single choice mode (radio buttons)
- Multiple choice mode (checked items)
- Focusable items (or rows)
Headers and Footers
- ListView.addHeaderView()
- ListView.addFooterView()
- must be called before setAdapter()
- isSelectable == Adapter.isEnabled() (see below)
- if you use header/footer, and then list.setAdapter(myAdapter); list.getAdapter() != myAdapter; Internally, ListView wraps one adapter in another one.
Selectors
- used to highlight the selected item
- Not shown in touch mode (there is no selection in touch mode!)
- Shown behind list items by default. Use android:drawSelectorOnTop=”true” if you want to change it.
Other features, DOs and DON’Ts
- android:transcriptMode, used to control the behaviour of the scrolling of the list when content is added.
- android:stackFromBottom, stacks items in reverse order, starts with the last item from the adapter
- these are useful for chats, messaging, talk, irc, etc.
- android:textFilterEnabled=”true”, filters and presents only the items that match what’s typed, you need to re-implement the Filter instance.
protected FilterResults performFiltering(CharSequence prefix) protected void publishResurts(CharSequence constraint, FilterResults results)
- for optimisation and efficient rendering purposes, all ListView items are turned into bitmaps, with a default black background. Use android:cacheColorHint=”myBackgroundColor”, or set it to #0 to disable it.
- Use android:smoothScrollbar=”false” to avoid scrollbar changing size when scrolling quickly through a list.
- DON’T – never set android:layout_height=”wrap_content”
- DON’T – never use ListView inside a ScrollView
- It is ok to have horizontal scroller with ListView using vertical scroller though.