Custom ArrayAdapter in Android

Custom ArrayAdapter in Android

Introduction

custom-adapter-row

In the basic version of the adapter, only one String data is used, which is placed in one of the predefined android layouts with one TextView.
Custom adapter differs from “ordinary” in that one list element is presented with more data and it is necessary to define a “more complex layout” that would accept and display that data. Such a customLayout row list can contain multiple subViews and widgets: image, text data (arranged in their specific positions)…

Custom ArrayAdapter creation procedure

a) Custom model (accepts more data for one list member)

In this example, one member of the list contains two data, so it is necessary to create a class that will be a “model” for creating objects that store the data of one member of an AdapterView:

User.java

Now within the Activity (Fragment) we can define the initial array of data by creating new objects as follows:

Activity

or to generate an ArrayList of User objects based on it:

User.java

To generate data from JSON, we use the following code that converts JSON into an ArrayList of User objects:

Activity

b) Creating an AdapterView

It is necessary to define the place where the data list will be placed as part of the Activity (fragment) layout. This part is the same as with a regular adapter, one ListView is created which will accept one custom row of each member.

activity_main.xml

c) Creating a custom layout for one list element

Now it is necessary to create a custom layout, which will be used to display more data of one list element. This part was not needed with the “regular” adapter, but now it is needed due to excess data, because we cannot use already predefined android layouts that accept only one data.

item_user.xml

d) Creating a Custom Adapter class

In the continuation of the application, we need to define the Adapter class on the basis of which a new adapter instance will be created as part of the Activity (Fragment). The entire process of converting a Java object into a View filled with data should be placed in this class.
We make custom adapter by creating a new class that extends the ArrayAdapter class. By extending the class, we get the getView() method, which we can “override” so that for each element of the list it takes data from the model and fills with it the previously defined Custom layout of the element.

Non-economical ArrayAdapter

This is an example of an ArrayAdapter that consumes a lot of resources when scrolling the screen, which is especially noticeable with large lists of data.

All the shortcomings of this procedure, as well as the improved procedure itself, which solves those shortcomings, are shown in the following example.

Improved ArrayAdapter (with ViewHolder pattern applied)

The first thing we need to pay attention to is that in the NON-economical example, a new “rowView” is created every time the getView() method is called.

If our list has a large number of elements that cannot fit on one screen, with each new scrolling, new Views are generated (each View is 1-2kB), which constantly increases the memory load.

convertView

It is precisely because of this problem that Android passes the “convertView” parameter to the getVIew() method. This parameter is used to “store” a View in it (eg our single rowView), which we will use again and again later. To prevent “inflating always the same XML” (which is an expensive operation) for each new element of the list, we will save the once created View within the convertView variable.

In the previous code, we check if there is already a saved View in the variable, and if there is none, we will create it (only the first time) as our rowView, because the convertView defined in this way will always be available to us as a parameter of the getView() method.

ViewHolder pattern

The next problem that occurs is the frequent calling of the findViewById() method for each subView of convertView. Frequent calls of the findViewById() method burden the system and reduce the performance of the application (especially if there is a lot of data).

To solve this problem, the so-called The “ViewHolder pattern” which relies on us already using convertView, and that we can simply target all of its subViews (just once) and thus avoid calling the findViewById() method all the time. Therefore, we create an inner static class which is often called ViewHolder.

We can associate the class members with the corresponding View by calling findViewById(), but note that we will do this only once and that is when we first define convertView:

In the previous example, we also used the setTag() method to save the ViewHolder object if we had already created it once, and then we later called the getTag() method and thus got the saved object back.
Using the ViewHolder pattern accelerates the population of the ListView, and with it we get smooth and fast loading of items. Its implementation allows to avoid using the “expensive” method findViewById() within the adapter. Full custom adapter example:

×

×

×

×

×

Method Description notifyItemChanged(int pos) Notify that item at position has changed. notifyItemInserted(int pos) Notify that item reflected at position has been newly inserted. notifyItemRemoved(int pos) Notify that items previously located at position have been removed from the data set. notifyDataSetChanged() Notify that the dataset has changed. Use only as a last resort.