ListView is a ViewGroup that displays a list of vertically scrollable items. The list items are automatically inserted into the list using an adapter
that is connected to a source, such as an array or a database query, and each item is converted into a row in the ListView.
BaseAdapter, as it's name implies, is the base class for so many concrete adapter implementations on Android. It is abstract and therefore, cannot be directly instantiated.
If your data source is an ArrayList
or array, we can also use the ArrayAdapter construct as an alternative. Note that ArrayAdapter
itself extends from BaseAdapter
.
To use the BaseAdapter with a ListView, a concrete implementation the BaseAdapter
class that implements the following methods must be created:
int getCount()
Object getItem(int position)
long getItemId(int position)
View getView(int position, View convertView, ViewGroup parent)
Before we create our custom BaseAdapter
implementation, we need to create the layout for the ListView row and also a model for the items in the ListView.
Each of our ListView rows will conatain an Item name
and Item description
, so our Model class is as follows:
Item.java
public class Item {
private String itemName;
private String itemDescription;
public Item(String name, String description) {
this.itemName = name;
this.itemDescription = description;
}
public String getItemName() {
return this.itemName;
}
public String getItemDescription() {
return itemDescription;
}
}
The xml file for the rows of the listview created in the res/layout
folder is shown below:
layout_list_view_row_items.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/text_view_item_name"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/text_view_item_description"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
public class CustomListAdapter extends BaseAdapter {
private Context context; //context
private ArrayList<Item> items; //data source of the list adapter
//public constructor
public CustomListAdapter(Context context, ArrayList<Item> items) {
this.context = context;
this.items = items;
}
@Override
public int getCount() {
return items.size(); //returns total of items in the list
}
@Override
public Object getItem(int position) {
return items.get(position); //returns list item at the specified position
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// inflate the layout for each list row
if (convertView == null) {
convertView = LayoutInflater.from(context).
inflate(R.layout.layout_list_view_row_items, parent, false);
}
// get current item to be displayed
Item currentItem = (Item) getItem(position);
// get the TextView for item name and item description
TextView textViewItemName = (TextView)
convertView.findViewById(R.id.text_view_item_name);
TextView textViewItemDescription = (TextView)
convertView.findViewById(R.id.text_view_item_description);
//sets the text for item name and item description from the current item object
textViewItemName.setText(currentItem.getItemName());
textViewItemDescription.setText(currentItem.getItemDescription());
// returns the view for the current row
return convertView;
}
}
The adapter can simply be used by instantiating it with the required paramters and set as the listview's adapter.
// Setup the data source
ArrayList<Item> itemsArrayList = generateItemsList(); // calls function to get items list
// instantiate the custom list adapter
CustomListAdapter adapter = new CustomListAdapter(this, itemsArrayList);
// get the ListView and attach the adapter
ListView itemsListView = (ListView) findViewById(R.id.list_view_items);
itemsListView.setAdapter(adapter);
To optimize the listview's performance, a new row layout should be inflated only when convertView == null
. This is because the adapter's getView
method is called whenever the listview needs to show a new row on the screen. The convertView
gets recycled in this process. Hence, the row layout should be inflated just once when convertView == null
, and it contents should be updated on subsequent getView
calls, instead of inflating a new row on each call which is very expensive.
One of the most common operation in Android is finding an inner view inside an inflated layout. This is usually done using the findViewById
method. This operation is not trivial especially when the lisview has to frequently call getView
when the list is scrolling.
The aim of the ViewHolder pattern, is to reduce the number of findViewById
calls in the adapter's getView
. A ViewHolder is practically a lightweight inner class that holds direct references to all inner views from a row. It is then stored as a tag in the row's view after inflating it. This way you’ll only have to use findViewById() when the layout is first created. Here’s a new implementation of our adapter's getView
with View Holder pattern applied:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(R.layout.layout_list_view_row_items, parent, false);
viewHolder = new ViewHolder(convertView);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
Item currentItem = (Item) getItem(position);
viewHolder.itemName.setText(currentItem.getItemName());
viewHolder.itemDescription.setText(currentItem.getItemDescription());
return convertView;
}
private class ViewHolder {
TextView itemName;
TextView itemDescription;
public ViewHolder(View view) {
itemName = (TextView)view.findViewById(R.id.text_view_item_name);
itemDescription = (TextView) view.findViewById(R.id.text_view_item_description);
}
}
The sample code for this tutorial can be found in this github repo.
This guide was originally drafted by Adegeye Mayowa to close #127.