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:
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:
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; } }