I have a problem that's been giving me grief for weeks now. I forgot about it for a while when I was updating to the new Facebook SDK, but now it's back to haunt my dreams.
I'm making basically a Facebook feed reader. It gets the JSON data from an API call and uses that to populate a ListView. Each time the custom adapter calls the getView() method, it checks to see what type of "status" it is. If it's a "photo" status, the view will have two images (a profile picture and the picture of the status update). I'm using nostra13's Universal Image Loader to load both images after inflating the ImageViews using a holder class to help recycle views. That's where I think my problem exists.
When I scroll through the list, all the profile pictures load fine and work great. But as I scroll, the status photos do a couple things:
1.) The all load the same image (I didn't have this problem before but I gave it to myself trying to fix the others).
2.) The images jump around and either disappear or appear in different views than they're supposed to.
3.) If the images load correctly and don't all show the same image, the images change as I scroll up and down, eventually all becoming the same image.
I'll post my getView() code (sorry it's kind of long).
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
try {
jsonObject = getItem(position);
jsonFrom = jsonObject.getJSONObject("from");
postType = jsonObject.getString("type");
posterName = jsonFrom.getString("name");
posterID = jsonFrom.getString("id");
posterPhoto = "http://graph.facebook.com/" + posterID
+ "/picture?type=square";
if (jsonObject.has("message")) {
posterMessage = jsonObject.getString("message");
} else {
posterMessage = jsonObject.getString("story");
}
} catch (JSONException e) {
e.printStackTrace();
}
if (postType.equals("status")) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.status_post_holder,null);
holder = new ViewHolder();
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.posterName = (TextView) convertView.findViewById(R.id.posterName);
holder.posterMessage = (TextView) convertView.findViewById(R.id.posterMessage);
holder.posterProfilePhoto = (ImageView) convertView.findViewById(R.id.posterProfilePic);
if (jsonObject != null) {
holder.posterName.setText(posterName);
if (posterMessage != null) {
holder.posterMessage.setText(posterMessage);
} else {
holder.posterMessage.setText("No message for this post.");
}
profileImageLoader.displayImage(posterPhoto,
holder.posterProfilePhoto);
}
}
else if (postType.equals("photo")) {
if (convertView == null) {
try {
posterImageURL = jsonObject.getString("picture");
} catch (JSONException e) {
e.printStackTrace();
}
LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.photo_post_holder, null);
holder = new ViewHolder();
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.posterName = (TextView) convertView.findViewById(R.id.posterName);
holder.posterMessage = (TextView) convertView.findViewById(R.id.posterMessage);
holder.posterProfilePhoto = (ImageView) convertView.findViewById(R.id.posterProfilePic);
holder.posterImage = (ImageView) convertView.findViewById(R.id.posterImage);
if (jsonObject != null) {
holder.posterName.setText(posterName);
if (posterPhoto != null) {
profileImageLoader.displayImage(posterPhoto,holder.posterProfilePhoto);
}
if (posterImageURL != null) {
pictureImageLoader.displayImage(posterImageURL,holder.posterImage);
}
if (posterMessage != null) {
holder.posterMessage.setText(posterMessage);
} else {
holder.posterMessage.setText("No message for this post.");
}
}
}
else if (postType.equals("link")) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.status_post_holder,null);
holder = new ViewHolder();
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.posterName = (TextView) convertView.findViewById(R.id.posterName);
holder.posterMessage = (TextView) convertView.findViewById(R.id.posterMessage);
holder.posterProfilePhoto = (ImageView) convertView.findViewById(R.id.posterProfilePic);
if (jsonObject != null) {
holder.posterName.setText(posterName);
if (posterMessage != null) {
holder.posterMessage.setText(posterMessage);
} else {
holder.posterMessage.setText("No message for this post.");
}
profileImageLoader.displayImage(posterPhoto,holder.posterProfilePhoto);
}
}
else {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.status_post_holder,null);
holder = new ViewHolder();
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.posterName = (TextView) convertView.findViewById(R.id.posterName);
holder.posterMessage = (TextView) convertView.findViewById(R.id.posterMessage);
holder.posterProfilePhoto = (ImageView) convertView.findViewById(R.id.posterProfilePic);
if (jsonObject != null) {
holder.posterName.setText(posterName);
if (posterMessage != null) {
holder.posterMessage.setText(postType);
} else {
holder.posterMessage.setText("No message for this post.");
}
profileImageLoader.displayImage(posterPhoto,holder.posterProfilePhoto);
}
}
return convertView;
}
I'm sure it has something to do with Android recycling views and being efficient, but I can't figure out where I'm making the error. It's also weird that the problem only exists for the one image view. Any help would be hot. Thanks.
UPDATE: We discovered it was an error with my JSON parsing. You have to use setTag() to retain the image specific to that line item. The adapter was trying to set a tag of null to the view, which caused the NPE.
UPDATE AGAIN: After digging some more, it looks like it's the ImageView itself that's giving a null value as if it isn't finding the correct layout. I was using two different xml layouts for the different types of posts. So now it looks like the best solution would be to use the same layout and just conditionally inflate different elements in each view depending on the status type.