UPDATE: Issue now fixed in cleaner way, please see comments below.
I ran into an interesting 'quirk' or as I would call it a 'bug' in the way that Android recycles views in GridView (and presumably the same issue applies to ListView too).
Say you have an Adaptor which supplies two different views - one is a simple ImageView and one is a more complex layout inflated from xml. The android sdk docs and examples suggest that you can override getItemViewType() and getViewTypeCount() in order that android will sent the correct type of view to your getView() method for recycling. Like so:
private class ImageAdapter extends BaseAdapter {
@Override
public int getViewTypeCount() {
return 2;
}
@Override
public int getItemViewType(int position) {
return getDirItem(position).isDir()? TYPE_DIR : TYPE_PIC;
}
@Override
public View getView(int position, View convertView, ViewGroup container) {
if (getItemViewType(position) == TYPE_DIR) { // Folder
if (convertView == null) {
LayoutInflater vi = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.folder_grid_entry, container, false);
}
convertView.setLayoutParams(mImageViewLayoutParams);
// Populate view here ...
return convertView;
} else { // Pic
ImageView imageView;
if (convertView == null) {
imageView = new ImageView(mContext);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
} else {
imageView = (ImageView)convertView;
}
imageView.setLayoutParams(mImageViewLayoutParams);
// load the image here (in a background thread) ...
return imageView;
}
}
However, this doesn't work. Android will occasionally send the wrong type of view in the convertView parameter to getView() resulting in some strange errors. It seems that the whole view type business is no guarantee that getView() will receive the right view type, at best Android treats view type as a 'hint'.
To be fair the Android docs
here do say:
"You should check that this view is non-null and of an appropriate type before using. If it is not possible to convert this view to display the correct data, this method can create a new view."
However, in the very next sentence they go on to say:
"Heterogeneous lists can specify their number of view types, so that this View is always of the right type (see getViewTypeCount() and getItemViewType(int))
It appears the the documentation is wrong in this regard, the view is
not always of the right type and it is essential to check this like so:
public View getView(int position, View convertView, ViewGroup container) {
if (getItemViewType(position) == TYPE_DIR) { // Folder
if ((convertView == null) || (convertView.getClass() != LinearLayout.class)) {
LayoutInflater vi = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.folder_grid_entry, container, false);
}
convertView.setLayoutParams(mImageViewLayoutParams);
// Populate view here ...
return convertView;
} else { // Pic
ImageView imageView;
if ((convertView == null) || (convertView.getClass() != ImageView.class)) {
imageView = new ImageView(mContext);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
} else {
imageView = (ImageView)convertView;
}
imageView.setLayoutParams(mImageViewLayoutParams);
// load the image here (in a background thread) ...
return imageView;
}
}