I am using a getView in an adapter where I am creating an imageview and making that equal to convertView where the view has already been initialized before. It contains image thumbnails, some of which represent videos.
@Override
public View getView(int position, View convertView, ViewGroup container) {
// First check if this is the top row
if (position < mNumColumns) {
if (convertView == null) {
convertView = new View(mContext);
}
// Set empty view with height of ActionBar
//convertView.setLayoutParams(new AbsListView.LayoutParams(
// LayoutParams.MATCH_PARENT, mActionBarHeight));
return convertView;
}
// Now handle the main ImageView thumbnails
ImageView imageView;
if (convertView == null) { // if it's not recycled, instantiate and initialize
imageView = new RecyclingImageView(mContext);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setLayoutParams(mImageViewLayoutParams);
} else { // Otherwise re-use the converted view
imageView = (ImageView) convertView;
}
// Check the height matches our calculated column width
if (imageView.getLayoutParams().height != mItemHeight) {
imageView.setLayoutParams(mImageViewLayoutParams);
}
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
if(images.get(position - mNumColumns).getUriString().contains("video")){
//display video icon
}
else
{
//don't display video icon
}
// Finally load the image asynchronously into the ImageView, this also takes care of
// setting a placeholder image while the background thread runs
if (images != null && !images.isEmpty())
mImageFetcher.loadImage(images.get(position - mNumColumns).getUriString()/*.imageUrls[position - mNumColumns]*/, imageView);
return imageView;
}
The thumbnails do not have a "play" button on them to designate that they are videos, so in those cases I need to add a play button, programmatically.
Typically I use a viewholder pattern with an inflated layout, I am not doing that in this case because I actually don't want some things in memory.
So instead I want to programmatically make a RelativeLayout as the root view of each cell (mRelativeLayout = (RelativeLayout)convertView
) and add the imageview and playbutton imageview into that convertview
How do I do that? It requires modification of this statement but I'm not sure how to initialize all the re-used views
} else { // Otherwise re-use the converted view
imageView = (ImageView) convertView;
}
I would make your getView()
always return a RelativeLayout
object (which I call containerView
below) to which you add your ImageView(s)
as children.
The only complication here is that you need to give these children identifiers so that you can retrieve them from a recycled convertView
later. Note that I used the built-in, static View.generateViewId()
for this, which is API level 17. If you need it to work pre-API-level-17 you can create your own ids using unique integers (such as 1, 2, etc.) -- just make sure they aren't greater than 0x0FFFFFF
. Update: I added code that I use for this below.
See the comments I added in several points below.
@Override
public View getView(int position, View convertView, ViewGroup container) {
// First check if this is the top row
if (position < mNumColumns) {
if (convertView == null) {
convertView = new View(mContext);
}
// Set empty view with height of ActionBar
//convertView.setLayoutParams(new AbsListView.LayoutParams(
// LayoutParams.MATCH_PARENT, mActionBarHeight));
return convertView;
}
// Now handle the main ImageView thumbnails
RelativeLayout containerView;
ImageView imageView;
ImageView videoIconView; // TODO: or whatever type you want to use for this...
if (convertView == null) { // if it's not recycled, instantiate and initialize
containerView = new RelativeLayout(mContext);
// TODO: The layout params that you used for the image view you probably
// now want to use for the container view instead...
imageView.setLayoutParams(mImageViewLayoutParams); // If so, you can change their name...
imageView = new RecyclingImageView(mContext);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
//imageView.setLayoutParams(mImageViewLayoutParams); // This probably isn't needed any more.
// Generate an Id to use for later retrieval of the imageView...
// This assumes it was initialized to -1 in the constructor to mark it being unset.
// Note, this could be done elsewhere in this adapter class (such as in
// the constructor when mImageId is initialized, since it only
// needs to be done once (not once per view) -- I'm just doing it here
// to avoid having to show any other functions.
if (mImageId == -1) {
mImageId = View.generateViewId();
}
imageView.setId(mImageId);
containerView.addView(imageView, RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT);
// NOTE: At this point, I would personally always just add the video icon
// as a child of containerView here no matter what (generating another unique
// view Id for it, mVideoIconId, similar to how was shown above for the imageView)
// and then set it to either VISIBLE or INVISIBLE/GONE below depending on whether
// the URL contains the word "video" or not.
// For example:
vidoIconView = new <whatever>;
// TODO: setup videoIconView with the proper drawable, scaling, etc. here...
if (mVideoIconId == -1) {
mVideoIconId = View.generateViewId();
}
videoIconView.setId(mVideoIconId);
containerView.addView(videoIconView, RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
final RelativeLayout.LayoutParams layout = ((RelativeLayout.LayoutParams)containerView.getLayoutParams());
layout.addRule(RelativeLayout.LayoutParams.CENTER_HORIZONTAL); // ... or whatever else you want
layout.addRule(RelativeLayout.LayoutParams.ALIGN_PARENT_BOTTOM); // ... or whatever else you want
} else {
// Otherwise re-use the converted view
containerView = (RelativeLayout) convertView;
imageView = containerView.findViewById(mImageId);
videoIconView = containerView.findViewById(mVideoIconId); // see comment above
}
// Check the height matches our calculated column width
if (containerView.getLayoutParams().height != mItemHeight) {
containerView.setLayoutParams(mImageViewLayoutParams);
}
if(images.get(position - mNumColumns).getUriString().contains("video")){
//display video icon
// see comment above, here you can probably just do something like:
videoIconView.setVisibility(View.VISIBLE);
}
else
{
//don't display video icon
videoIconView.setVisibility(View.GONE); // could also use INVISIBLE here... up to you.
}
// Finally load the image asynchronously into the ImageView, this also takes care of
// setting a placeholder image while the background thread runs
if (images != null && !images.isEmpty())
mImageFetcher.loadImage(images.get(position - mNumColumns).getUriString()/*.imageUrls[position - mNumColumns]*/, imageView);
return containerView;
}
Update:
In response the question in the comments, I use something like this (in my custom "ViewController" base class):
private static int s_nextGeneratedId = 1;
/**
* Try to do the same thing as View.generateViewId() when using API level < 17.
* @return Unique integer that can be used with setId() on a View.
*/
protected static int generateViewId() {
// AAPT-generated IDs have the high byte nonzero; clamp to the range under that.
if (++s_nextGeneratedId > 0x00FFFFFF)
s_nextGeneratedId = 1; // Roll over to 1, not 0.
return s_nextGeneratedId;
}
Note that you do not need a unique view id for every single cell in your grid. Rather, you just need it for each type of child view that you might want to access using findViewById()
. So in your case, you're probably going to need just two unique ids. Since the view ids auto-generated from your xml layout files into your R.java
typically are very large, I've found it convenient just to use low numbers for my hand-generated ids (as shown above).