I have a ListView that is getting populated by my custom BaseAdapter with views that contain a couple of TextViews and ImageButtons. If the user selects an item from a PopupMenu on one of the list items, it changes the text of an associated TextView on that list item. This is working great for all of the items in my ListView except the first one. The first item never updates the TextView for some reason. I suspect it has something to do with my convertView for that item not getting reset, but I'm not sure how to go about fixing it.
Here is my setup. I have a ListView that is getting populated by a custom BaseAdapter. In my BaseAdapter, I have this for my getView()
:
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null) {
IMOModGroup modGroup = modGroups.get(position);
convertView = modGroup.getConvertView(parent);
}
return convertView;
}
IMOModGroup is a special data structure that holds all the states and data for each list item. So when the convertView comes in null, the BaseAdapter requests a new convertView from the IMOModGroup for that position. Here's the IMOModGroup method that creates the new convertView:
public View getConvertView(ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.modifier_list_item, parent, false);
//...
TextView selectedModText = (TextView) convertView.findViewById(R.id.modName);
selectedModText.setText(R.string.no_modifier_selected);
selectedModText.setTextColor(context.getResources().getColor(R.color.grayed_out_text_color));
//...
return convertView;
}
The user can open a PopupMenu on each list item and make a selection. When they do this, the TextView in the above code is changed to reflect the selection with new text. That code looks like this:
public boolean onMenuItemClick(MenuItem item) {
String selectedMod = item.getTitle().toString();
TextView selectedModText = (TextView) convertView.findViewById(R.id.modName);
selectedModText.setText(selectedMod);
selectedModText.setTextColor(context.getResources().getColor(R.color.imo_blue_light));
//...
return false;
}
All of this is working great on every item in the ListView except for the very first one, which never updates to reflect the TextView change. I've tested to make sure the TextView text is actually getting changed and can be called and viewed at a later time, and it's being set correctly. So it seems like the data is correct, but the view is not getting updated.
I'm pretty sure the problem is that my convertView is not getting reset, so the ListView is just continuing to use the old view with the old text. I tried calling notifyDataSetChanged()
on the BaseAdapter after I set the text, and that didn't work.
This question looks promising: First item in list view is not displaying correctly
But I'm not sure if that's my problem or not, and I'm not sure how I would apply that to my scenario.
Any ideas? I've read through a few similar questions on SO and Google, but none of them really seemed to be relevant to my specific scenario. I've been using ListViews and Adapters for quite a while now and this is the first time I've seen something like this. Thanks for your help.
EDITS IN RESPONSE TO COMMENTS
Here is the complete BaseAdapter code...
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import java.util.ArrayList;
public class IMOModListAdapter extends BaseAdapter {
private Context context;
private ArrayList<IMOModGroup> modGroups;
public IMOModListAdapter(Context context, ArrayList<IMOModGroup> modGroups) {
this.context = context;
this.modGroups = modGroups;
}
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null) {
IMOModGroup modGroup = modGroups.get(position);
convertView = modGroup.getConvertView(parent);
}
return convertView;
}
public Object getItem(int position) {
return 0;
}
public long getItemId(int position) {
return 0;
}
public int getCount() {
return modGroups.size();
}
}
FURTHER TESTING
As I digged deeper into my debugging, I realized that when the TextView for element 1 in the list is changed, getView()
is not even called at all. But when any of the other elements are changed, getView()
is called as normal.
As a sanity check, I tried changing the text of the first list item from the outside, from the parent Activity itself. If I try to change the first item, it does nothing, but if I try to change any of the other list items, it works fine! This is so bizarre! This is what I tried:
//Set the text of the first item. This does NOTHING and the TextView
//in the item remains unchanged
IMOModGroup modGroup = modGroups.get(0);
modGroup.setTestText("This is some test text");
modList.notifyDataSetChanged();
//Set the text of the second item. This works completely fine!!!!!
IMOModGroup modGroup = modGroups.get(1);
modGroup.setTestText("This is some test text");
modList.notifyDataSetChanged();
Well I found a solution to this issue. Just researching random SO questions about BaseAdapters and getView()
, I finally came across this post by Romain Guy in which he states that you should never use wrap_content
for the layout_height
of a ListView, since it causes the ListView to call the adapter's getView()
tons of times just to create dummy views which it uses to measure the height of the content. It turns out this was causing my issue, so changing my layout_height
to match_parent
did the trick.
Unfortunately, now this causes a new problem since I can't use match_parent
since it will hide other views in my parent layout. But at least the first list item is now updating correctly.
UPDATE:
I learned how to manually and dynamically set the height of my ListView after its adapter is populated. I used the method below, and hopefully this will help someone out running into similar issues. This made it so I didn't have to use wrap_content
but I could still set the ListView
height to only be as big as its content.
private void setModListHeight() {
int numberOfItems = modGroups.size(); //the ArrayList providing data for my ListView
int totalItemsHeight = 0;
for(int position = 0; position < numberOfItems; position++) {
View item = modListAdapter.getView(position, null, modList);
item.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
totalItemsHeight += item.getMeasuredHeight();
}
int totalDividersHeight = modList.getDividerHeight() * (numberOfItems - 1);
ViewGroup.LayoutParams params = modList.getLayoutParams();
params.height = totalItemsHeight + totalDividersHeight;
modList.setLayoutParams(params);
modList.requestLayout();
}