I\'m using a SimpleCursorAdapter
and a ListView
to show some data loaded with a Loader.
Inside the cursor
I have items with a in
It's important when working with ListViews, especially complicated ones like you describe, to handle view recycling properly. The BaseAdapter
class, a superclass of SimpleCursorAdapter
has a few methods that you can override to achieve the effect you want while using minimal resources. I've never used a SimpleCursorAdatper before, so this is written with a normal CursorAdapter in mind, but you can use this with any Adapter class that overwrites BaseAdapter.
ListViews in Android behave in a very specific way to lower memory cost. As you scroll through a ListView, the views for items that move off the screen are placed in a small pool of views. The convertView
parameter is taken from this pool. They do this because keeping each list item View in memory does not scale well and can quickly cause an OutOfMemory exception. The getView()
method is where you take these views and configure them for the current list item. Usually you'll have a line that looks like this:
if(convertView == null)
convertView = layoutInflator.inflate(R.layout.list_item, null);
In this case, if convertView is not null, we know it was previously inflated. We don't want to re-inflate it, because this is a costly action and getView should just be quickly populating the views with data before they are displayed.
Now, in your case, there are two potential inflations for convertView. Rather than re-inflating the view every time (very bad) or using some kind of hack with unique resource ids for each view (better, but not ideal), we can override two methods in the base class to ensure convertView is always the correct type. These two methods are getItemViewCount()
and getItemViewType(int position)
.
getItemViewCount()
is used by the Adapter to determine how many pools of views it should maintain for the list. Overriding it is simple, and in your case would look something like this.
@Override
public int getViewTypeCount()
{
return 2; //Even though you have four cases, there are only 2 view types.
}
getItemViewType(int position)
is used by the Adapter BEFORE getView is called to decide which pool convertView should come from. In here, you want a switch or if/else statement that checks your underlying data source for which view type it is and return it. (Note, per the Android documentation, the return value here has to be between 0 and getViewTypeCount() -1, so in your case either 0 or 1.)
@Override
public int getItemViewType(int position)
{
Item item = getItem(position) //Or however you're getting the data associated with a particular list position
switch(item.myInt)
{
//I simplified this a bit, basically, check your int, if it's the first type, return 0 for your first layout type, else return 1 for your second.
case(0):
case(1):
return 0;
case(2):
case(3):
return 1;
}
}
Now, finally, we'll modify getView to do the initial layoutInflation so that you have the correct Views in your pools.
@Override
public View getView(int position, View convertView, ViewGroup viewParent)
{
//if convertView is not null, we got a view from the pool, just go on
if(convertView == null)
{
//This means we didn't have a view in the pool to match this view type. Inflate it and it will be placed in the proper pool when this list item is scrolled off the screen
if(getItemViewType(position) == 0)
convertView = layoutInflator.inflate(R.layout.list_item_type1, null);
else if(getItemViewType(position) == 1)
convertView = layoutInflator.inflate(R.layout.list_item_type2, null);
}
//Populate the view with whatever data you need here
//And finally....
return convertView;
}
ListViews and their Adapters are one of the most complicated things I've encountered in Android, but taking the time out to do it right will greatly improve the performance and user experience in your app. Good luck!
I think you only need to inflate the right layout on your getView(int position, View convertView, ViewGroup parent)
method:
MyItem item = getItem(position);
View vi = convertView;
if(vi == null){
switch(item.getStatus())
{
case 0:
vi = mInflater.inflate(R.layout.item1, null);
break;
case 1:
vi = mInflater.inflate(R.layout.item2, null);
break;
case 2:
vi = mInflater.inflate(R.layout.item3, null);
break;
}
//set viewholder ...
}else{
//get viewholder ...
}
// set values to views ...
Is this what you needed?