A Custom Adapter Java Class

Android Custom Lists: 1 2 3 4 5 6 7 8

In a previous lesson, we used an inbuilt adapter to fill our list with rows. This time, we're going to be creating our own. But it does the same job: to act as a middleman between the ListView and an array of values that you want for the list.

Right-click your package name in the Explorer on the left. Select New > Java Class again to bring up this dialogue box:

The Create New Class dialogue box

For the Name, enter CustomAdapter. We need to extend the BaseAdapter. So type BaseAdaper in the Superclass textbox. You should get a popup list where you can select the BaseAdapter item. Android Studio will then add android.widget.BaseAdapter to the textbox. Make sure you select the box at the bottom that says Show Select Overrides Dialog. You dialogue box should look like this:

Create New Class dialogue box extending the BaseAdapter

Click OK and you'll get another dialogue box popping up. This one allows you to select methods to include in your class, that way you don't have to type them all out yourself. Multi-select all the ones at the bottom:

Selecting methods to override

We've selected getCount, getItem, getItemId, and getView. Click OK to create a class that looks like this:

A Custom Adapter Java class with override methods

We need to set up two private field variables at the top of the class. One of these will be an ArrayList. The items in the array will be our RowItem class. The other field variable we need to set up is for a LayoutInflater object. The LayoutInflater will be used to create a row from that XML file we created earlier. It inflates an XML layout and attaches it to a View, the ListView, in our case.

Add these two lines, then:

private ArrayList<RowItem> singleRow;
private LayoutInflater thisInflater;

The top of your code will look like this:

Adding field variables to the Java class

Notice that Android Studio has added two new import lines for us:

import android.view.LayoutInflater;
import java.util.ArrayList;

One thing the dialogue boxes didn't add for us was a Class Constructor. We can add one for ourselves. Add this just below the two lines you've just added:

public CustomAdapter(Context context, ArrayList<RowItem> aRow) {

this.singleRow = aRow;
thisInflater = ( LayoutInflater.from(context) );

}

Our Constructor passes in a context (which screen or View we are talking about, if you like), and an array of Row Items. The two variables context and aRow are then used to store something in the singleRow and thisInflater variables.

Here's what the top of your code should look like now (we've left out the package name):

Custom Adapter Java class with Constructor added

The getCount method is used to get how many items are in your array. For ArrayLists, you use the size method. Add this line to getCount, instead of return 0:

public int getCount() {

return singleRow.size( );

}

The getItem method only needs one line, too. This only needs to return what position we are at in the array. Add this, then, instead of return null:

public Object getItem(int position) {

return singleRow.get( position );

}

The getItemId method only needs to return the position:

public long getItemId(int position) {

return position;

}

Your class should look like this, so far:

The Custom Adapter Java class so far

The getView method is where most of the work is done. Remember, the Adapter is creating rows for us, and each row has an ImageView, and two TextViews

In getView, we need to grab a reference to our list_view_row.xml file. We can then get references to each View in our XML file

Between the curly brackets of getView, delete return null and type return convertView instead:

public View getView(int position, View convertView, ViewGroup parent) {

return convertView;

}

The convertView variable will hold all the rows for our ListView.

Just before this, enter the following:

public View getView( int position, View convertView, ViewGroup parent ) {

if (convertView == null) {

}

return convertView;

}

We need to check if convertView has a value of null. Only then do we need to grab a reference to our XML file. So add this line to the if statement:

if (convertView == null) {

convertView = thisInflater.inflate( R.layout.list_view_row, parent, false );

}

We use our LayoutInflater object to get a reference to the xml file called list_view_row. The parent and false at the end mean the root ViewGroup, which is the parent Layout, and whether you want to attach it to this root, which you don't.

At the end of this if statement, you'll have a single row inside the convertView variable.

We can grab our views now, which can be references with the convertView variable. To get a reference to the fist TextView, add this line inside of the if statement:

TextView theHeading = (TextView) convertView.findViewById(R.id.textHeading);

And add this line to grab the SubHeading TextView:

TextView theSubHeading = (TextView) convertView.findViewById(R.id.textSubHeading);

To grab the ImageView, add this line:

ImageView theImage = (ImageView) convertView.findViewById(R.id.imageView);

Your getView method should look like this:

Overriding the getView method in Android

We now need some text to go on the TextViews, and an image to place inside of the ImageView. Add these four lines inside the if statement:

RowItem currentRow = (RowItem) getItem(position);

theHeading.setText( currentRow.getHeading() );
theSubHeading.setText( currentRow.getSubHeading() );
theImage.setImageResource(currentRow.getSmallImageName() );

First, we need know which row item we're currently on. We can use getItem( position ) for this. We've set up a variable called currentRow to get the all the items at a particular position. The variable needs to be an object created from our RowItem class.

The TextView called theHeading can then have its text set. In between its round brackets, we have this:

currentRow.getHeading()

Here, we're using one of those getters we set up in our RowItem Class. We set one up called getHeading.

The second TextView, which we called theSubHeading, uses the getter we set up with the name getSubHeading.

To set an image for the ImageView, we have this:

theImage.setImageResource( currentRow.getSmallImageName() );

An ImageView has a method called setImageResource. In between its round brackets, we're using the getSmallImageName getter we set up.

Here's what your getView method should look like:

Getting references to ImageViews and TextViews

If you're having trouble understanding the code for the getView method, just keep in mind what we're doing here: grabbing a reference to our list_view_row XML file, and then setting values for it. Once all the values are set, we return the whole of a row view.

We don't need to do any more with this CustomAdapter class, so you can close it down, if you like.

In the next lesson, we'll start using this new Custom Adapter class to create rows in our ListView.