3

I am building a ListView with sections according to the technique described at http://bartinger.at/listview-with-sectionsseparators/ . But I would like to extend it by reusing convertView for the non-section items. However, I am finding that the convertView variable is null each time getView() method is entered. Could someone explain why this is the case?

ViewHolder holder;
final ListViewItem item = items.get(position);

if (item.isSection()) {
    Section section = (Section)item;

    convertView = inflater.inflate(R.layout.section, null);
    TextView title = (TextView) convertView.findViewById(R.id.section_title);
    title.setText(section.title);
} else {
    if (convertView == null) {
        Log.d("Adapter", "convertView was null");
    }

    Server server = (Server)item;

    convertView = inflater.inflate(R.layout.server_row, null);
    holder = new ViewHolder();
    holder.serverName = (TextView) convertView.findViewById(R.id.server_name);
    holder.serverStatusIcon = (ImageView)convertView.findViewById(R.id.server_status_icon);
    convertView.setTag(holder);

    holder.serverName.setText(server.name);
}

return convertView;

The list is being created and displayed without errors and contains both sections and non-section items just fine.

Marc
  • 145
  • 1
  • 3
  • 11

3 Answers3

5

Are you implementing correctly

getItemViewType (int position) ?

See from Android's documentation:

Returns

An integer representing the type of View. Two views should share the same type if one can be converted to the other in getView(int, View, ViewGroup). Note: Integers must be in the range 0 to getViewTypeCount() - 1. IGNORE_ITEM_VIEW_TYPE can also be returned.

So maybe the convertView is always null because the adapter doesn't know which items belong together, so it doesn't know which ones pass to be recycled...

Try this:

@Override
public int getItemViewType(int position) {
    if (((MyItem)getItem(position)).isHeader()) {
        return 1;
    } else {
        return 0;
    }
}

@Override
public int getViewTypeCount() {
    return 2;
}

The index which you return in getItemViewType is just an identifier to group headers and not-headers together.

In this case you have to implement a method "isHeader" (or analogous) in your model items.

Community
  • 1
  • 1
User
  • 31,811
  • 40
  • 131
  • 232
  • I had looked at those methods in the API, but it doesn't say they are required. I expected to receive whatever view I returned last time. However, I did add these methods (as seen in your post) and am still receiving null convertViews. – Marc Jun 20 '12 at 19:28
  • I guess you can't get the view from the last time, because you are very probably still using it (previous element in the list). The framework decides when it passes to you "old" views to be recycled. Now, if you are using different kind of items, the framework can't pass you *any* view, it has to know that the recycled (old) view is compatible to the one you are creating. And these methods help with that. They are not required because it doesn't break anything if you don't implement it. But then you can't recycle the views and get always null. – User Jun 20 '12 at 19:34
  • (It's all only my theory). But why you don't try to implement and see if it helps. – User Jun 20 '12 at 19:35
  • The theory is not correct, but it did help me arrive at the answer (posted). Thank you! – Marc Jun 20 '12 at 19:53
0

I just went through this with a GridView that I created. I had problems when I tried to assign a newly inflated view to convertView. The generic structure I adopted was

public View getView(int position, @Nullable View convertView, ViewGroup parent) {

    View newView = null;
    TextView someText;

    // Test to see if there is already a view, if not create one, else use what is
    // already existant in convertView
    if (null == convertView) {
        // inflate your view type into newView here
        newView = myInflater.inflate(R.layout._________);

        // Get all of your subviews you wish to edit here from newView
        someText = (TextView)newView.findViewById(R.id._______);
    }else{
        // Get all of your subviews you wish to edit here from convertView
        someText = (TextView)convertView.findViewById(R.id._______);
    }

    // Perform all re-alignments, view layouts etc... here

    // Perform all updating of subviews data here

    // Return structure
    if (null == convertView) {
        return newView;
    } else {
        return convertView;
    }
}

Hope this helps!

Jared Burrows
  • 54,294
  • 25
  • 151
  • 185
trumpetlicks
  • 7,033
  • 2
  • 19
  • 33
  • If I understand correctly, you're saying the issue may be that a self declared View variable needs to be used the first time around. I tested this, and it appeared to make no difference. I seem to be able to inflate to converView without issues. Thank you for suggestion, however. – Marc Jun 20 '12 at 19:38
0

Thank you to Ixx for jogging my mind: what I hadn't noticed was that my list was so short it never actually filled the screen so no recycling was taking place.

For completeness sake, I will add that if you create multiple view types, getView() does return convertView - even if you do not override getItemViewType() and getViewTypeCount() - according to their default implementation (below). Of course, it is likely not the behaviour you want.

public int getItemViewType(int position) {
    return 0;
}

public int getViewTypeCount() {
    return 1;
}
Marc
  • 145
  • 1
  • 3
  • 11