Sometimes, we need to have the flexibility to completely customize the way a datatype is rendered for each item. To do this, we can make use of the BaseAdapter
type and custom item layouts.
If we want to specify an actual custom type, then we can create an adapter, derived from the BaseAdapter
type:
public class Person { public int Id { get; set; } public string Name {get;set;} public string Status {get;set;} public bool IsMale { get; set; } }
BaseAdapter<T>
adapter:public class PeopleAdapter : BaseAdapter<Person> { public override int Count { get; } public override Person this[int index] { get; } public override long GetItemId(int position) { } public override View GetView( int position, View convertView, ViewGroup parent) { } }
private readonly Context context; private readonly List<Person> data; public PeopleAdapter(Context context, List<Person> data) { this.data = data; this.context = context; }
Count
, which simply returns the size of the collection, and the indexer, which returns the particular data item:public override int Count { get { return data.Count; } } public override Person this[int index] { get { return data[index]; } }
GetItemId()
method, which we use to return an ID or just the item's position:public override long GetItemId(int position) { return data[position].Id; }
GetView()
method. First, we get hold of the data item for this list item:Person person = data[position];
convertView
parameter, and if we can't, we inflate or create a new view:if (convertView == null) { var inflater = LayoutInflater.From(context); convertView = inflater.Inflate( Resource.Layout.ListItemLayout, parent, false); }
var firstRow = convertView.FindViewById<TextView>( Resource.Id.firstRow); var icon = convertView.FindViewById<ImageView>( Resource.Id.icon);
int image = person.IsMale ? Resource.Drawable.male : Resource.Drawable.female; icon.SetImageResource(image); firstRow.Text = person.Name;
return convertView;
We can put any data into a list view, but we do need to let the list view know how to handle as well as present the data. Our data can come from any source, such as a database, file, or a collection. To do this, we use a list adapter, which inherits from IListAdapter
, or more specifically, the generic BaseAdapter<T>
type.
This adapter has four members that we need to override, three of which are very simple and, usually, require only a single line of code.
The Count
property is very simple and it just returns the number of items that are in the data. The indexer is also very simple and simply returns the data item for a particular position in the data collection.
In the case of the GetItemId()
method, we should try and return the item's ID. If the item does not have an ID that we can use, we can just return the item's position, the method's parameter.
The GetView()
method is where we can translate the data item into a view for a list item. Here, we can obtain the data item and display it in any form of our choice. Once we have the data item, which we obtain using the position parameter, we need a view to display it in.
Instead of creating a new view for each item, we should try to reuse views. An old view comes through to us via the convertView
parameter. This view is actually one of the items that we created previously but has scrolled off the screen. This view may be null
, for example, when the items are first rendered; so, we do have to construct the view before using it.
Reusing views reduces the amount of memory required to display the data as well as improving performance, especially when scrolling, as the list view does not have to reconstruct the view each time.
Once we have the view, either reused or created, we can then update the various subviews as we wish using the data for that item. The process of updating a view is exactly the same as for a normal view in the activity.
We can also override the nongeneric BaseAdapter
type. This type does not have the indexer, but instead has a GetItem()
method, which returns a Java.Lang.Object
element. There are a few steps in converting, or actually wrapping, a .NET object into a Java object, which the GetItem()
method on the generic adapter does for us. The generic adapter uses the value returned by the indexer and wraps it in a Java.Lang.Object
element.