Using a BaseAdapter with arbitrary data

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.

How to do it...

If we want to specify an actual custom type, then we can create an adapter, derived from the BaseAdapter type:

  1. A list can bind to any data type collection, as long as we have an adapter that understands how to present each item. For example, we can bind to an arbitrary type:
    public class Person {
      public int Id { get; set; }
      public string Name {get;set;}
      public string Status {get;set;}
      public bool IsMale { get; set; }
    }
  2. The adapter that we will create can inherit from the generic 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) {
      }
    }
  3. Next, we add a constructor that provides us with access to a context and the data that we store in fields:
      private readonly Context context;
      private readonly List<Person> data;
    
      public PeopleAdapter(Context context, List<Person> data) {
        this.data = data;
        this.context = context;
      }
  4. We can now implement the simple property, 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]; }
    }
  5. There is also the 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;
    }
  6. Finally, we will implement the GetView() method. First, we get hold of the data item for this list item:
    Person person = data[position];
  7. Now that we have the data, we start constructing the view. First, we check whether we can reuse any old view from the 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);
    }
  8. Once we have the view, we can put the values from the data item into the various subviews in the item. We can access any subview of the item view just as we can for any other view:
    var firstRow = convertView.FindViewById<TextView>(
      Resource.Id.firstRow);
    var icon = convertView.FindViewById<ImageView>(
      Resource.Id.icon);
  9. Once we have the various views, we can assign the data to them:
    int image = person.IsMale
      ? Resource.Drawable.male
      : Resource.Drawable.female;
    icon.SetImageResource(image);
    firstRow.Text = person.Name;
  10. Lastly, we return the view:
    return convertView;

How it works...

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.

Tip

A data item's position in the adapter can be used as the ID if it does not have a specific ID.

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.

Tip

Views should be reused whenever possible to improve performance and memory usage.

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.

There's more...

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.

See also

  • The Using custom ListView items recipe
  • The Optimizing the ListView recipe
  • The Using section indexes recipe
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset