Custom ArrayAdapter with ListView in Android
In the previous article ArrayAdapter in Android with Example, it’s been discussed how the ArrayAdapter works and what are the data sources which can be attached to the ArrayAdapter with ListView. In this article, it’s been discussed how to implement custom ArrayAdapter with the ListView. Have a look at the following image in which a single view in the ArrayAdapter can be customized.

Steps to Implement the Custom ArrayAdapter
Step 1: Create an Empty Activity project
Create an Empty Activity Android studio project. Refer to Android | How to Create/Start a New Project in Android Studio?
And make sure to select the programming as Java or Kotlin.
Step 2: Working with the activity_main.xml
In the activity_main.xml file, the root view is ListView. Invoke the following code into the activity_main.xml file and mention the appropriate ID for the ListView.
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<ListView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
</ListView>
Layout:
Step 3: Creating a custom View for ListView
Under layout, the folder creates a layout as list_item.xml and invokes the following code.
list_item.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
tools:ignore="UselessParent">
<ImageView
android:id="@+id/imageView"
android:layout_width="84dp"
android:layout_height="84dp"
android:padding="16dp"
tools:ignore="ContentDescription" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="vertical">
<TextView
android:id="@+id/textView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="bottom|left"
android:textColor="@android:color/black"
android:textSize="18sp"
android:textStyle="bold"
tools:ignore="RtlHardcoded" />
<TextView
android:id="@+id/textView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:gravity="top|left"
android:textColor="@android:color/black"
android:textSize="14sp"
tools:ignore="RtlHardcoded" />
</LinearLayout>
</LinearLayout>
Layout:
For every single item in the ListView this layout creates the following view for every single item in the array adapter.

Step 4: Create a custom class for custom layout
By creating this custom class we invoke the getter and setter manually for the list_item layout. Create a custom class called NumbersView.kt under the package folder of the Application. And invoke the following code.
NumberView Class:
public class NumbersView {
// the resource ID for the imageView
private int ivNumbersImageId;
// TextView 1
private String mNumberInDigit;
// TextView 1
private String mNumbersInText;
// create constructor to set the values for all the parameters of the each single view
public NumbersView(int NumbersImageId, String NumbersInDigit, String NumbersInText) {
ivNumbersImageId = NumbersImageId;
mNumberInDigit = NumbersInDigit;
mNumbersInText = NumbersInText;
}
// getter method for returning the ID of the imageview
public int getNumbersImageId() {
return ivNumbersImageId;
}
// getter method for returning the ID of the TextView 1
public String getNumberInDigit() {
return mNumberInDigit;
}
// getter method for returning the ID of the TextView 2
public String getNumbersInText() {
return mNumbersInText;
}
}
package org.geeksforgeeks.demo
class NumbersView (
// Image View
val numbersImageId: Int,
// TextView 1
val numberInDigit: String,
// TextView 1
val numbersInText: String
)
Step 5: Now create a custom ArrayAdapter class of the type NumbersView
Under the same package name, create a NumbersViewAdapter.kt class of the type NumbersView which extends the ArrayAdapter class. And invoke the following code inside the NumbersViewAdapter.kt file. Comments are added for better understanding.
NumberViewAdapter Class:
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.ArrayList;
public class NumbersViewAdapter extends ArrayAdapter<NumbersView> {
// invoke the suitable constructor of the ArrayAdapter class
public NumbersViewAdapter(@NonNull Context context, ArrayList<NumbersView> arrayList) {
// pass the context and arrayList for the super
// constructor of the ArrayAdapter class
super(context, 0, arrayList);
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
// convertView which is recyclable view
View currentItemView = convertView;
// of the recyclable view is null then inflate the custom layout for the same
if (currentItemView == null) {
currentItemView = LayoutInflater.from(getContext()).inflate(R.layout.custom_list_view, parent, false);
}
// get the position of the view from the ArrayAdapter
NumbersView currentNumberPosition = getItem(position);
// then according to the position of the view assign the desired image for the same
ImageView numbersImage = currentItemView.findViewById(R.id.imageView);
assert currentNumberPosition != null;
numbersImage.setImageResource(currentNumberPosition.getNumbersImageId());
// then according to the position of the view assign the desired TextView 1 for the same
TextView textView1 = currentItemView.findViewById(R.id.textView1);
textView1.setText(currentNumberPosition.getNumberInDigit());
// then according to the position of the view assign the desired TextView 2 for the same
TextView textView2 = currentItemView.findViewById(R.id.textView2);
textView2.setText(currentNumberPosition.getNumbersInText());
// then return the recyclable view
return currentItemView;
}
}
package org.geeksforgeeks.demo
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.ImageView
import android.widget.TextView
// Adapter for displaying a list of NumbersView objects in a ListView.
class NumbersViewAdapter(context: Context, private val numbersList: List<NumbersView>)
: ArrayAdapter<NumbersView>(context, 0, numbersList) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
// convertView: Recyclable view (may be null).
var itemView = convertView
val holder: ViewHolder
// If the recyclable view is null, inflate the
// custom layout and create a ViewHolder.
if (itemView == null) {
itemView = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false)
holder = ViewHolder(itemView)
// Store the ViewHolder in the view's tag.
itemView.tag = holder
}
else {
// Otherwise, retrieve the ViewHolder from the view's tag.
holder = itemView.tag as ViewHolder
}
// Get the NumbersView object for the current position.
// If getItem(position) returns null, return the itemView.
val currentNumber = getItem(position) ?: return itemView!!
// Assign the desired image to the ImageView.
holder.numbersImage.setImageResource(currentNumber.numbersImageId)
// Assign the desired text to TextView 1.
holder.textView1.text = currentNumber.numberInDigit
// Assign the desired text to TextView 2.
holder.textView2.text = currentNumber.numbersInText
// Return the recyclable view.
return itemView
}
// ViewHolder class to hold references
// to the views within a list item.
private class ViewHolder(view: View) {
val numbersImage: ImageView = view.findViewById(R.id.imageView)
val textView1: TextView = view.findViewById(R.id.textView1)
val textView2: TextView = view.findViewById(R.id.textView2)
}
}
Step 6: Working with the MainActivity.kt file
In this case, there is a need to create a custom ArrayList of all the items that are Image for ImageView, Text for TextView 1, Text for TextView 2.
MainActivity File:
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ListView;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// create a arraylist of the type NumbersView
final ArrayList<NumbersView> arrayList = new ArrayList<NumbersView>();
// add all the values from 1 to 15 to the arrayList
// the items are of the type NumbersView
arrayList.add(new NumbersView(R.drawable.geeks_logo, "1", "One"));
arrayList.add(new NumbersView(R.drawable.geeks_logo, "2", "Two"));
arrayList.add(new NumbersView(R.drawable.geeks_logo, "3", "Three"));
arrayList.add(new NumbersView(R.drawable.geeks_logo, "4", "Four"));
arrayList.add(new NumbersView(R.drawable.geeks_logo, "5", "Five"));
arrayList.add(new NumbersView(R.drawable.geeks_logo, "6", "Six"));
arrayList.add(new NumbersView(R.drawable.geeks_logo, "7", "Seven"));
arrayList.add(new NumbersView(R.drawable.geeks_logo, "8", "Eight"));
arrayList.add(new NumbersView(R.drawable.geeks_logo, "9", "Nine"));
arrayList.add(new NumbersView(R.drawable.geeks_logo, "10", "Ten"));
arrayList.add(new NumbersView(R.drawable.geeks_logo, "11", "Eleven"));
arrayList.add(new NumbersView(R.drawable.geeks_logo, "12", "Twelve"));
arrayList.add(new NumbersView(R.drawable.geeks_logo, "13", "Thirteen"));
arrayList.add(new NumbersView(R.drawable.geeks_logo, "14", "Fourteen"));
arrayList.add(new NumbersView(R.drawable.geeks_logo, "15", "Fifteen"));
// Now create the instance of the NumebrsViewAdapter and pass
// the context and arrayList created above
NumbersViewAdapter numbersArrayAdapter = new NumbersViewAdapter(this, arrayList);
// create the instance of the ListView to set the numbersViewAdapter
ListView numbersListView = findViewById(R.id.listView);
// set the numbersViewAdapter for ListView
numbersListView.setAdapter(numbersArrayAdapter);
}
}
package org.geeksforgeeks.demo
import android.os.Bundle
import android.widget.ListView
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity()
{
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// create a arraylist of the type NumbersView
val arrayList = ArrayList<NumbersView?>()
// add all the values from 1 to 15 to the arrayList
// the items are of the type NumbersView
arrayList.add(NumbersView(R.drawable.gfg_logo, "1", "One"))
arrayList.add(NumbersView(R.drawable.gfg_logo, "2", "Two"))
arrayList.add(NumbersView(R.drawable.gfg_logo, "3", "Three"))
arrayList.add(NumbersView(R.drawable.gfg_logo, "4", "Four"))
arrayList.add(NumbersView(R.drawable.gfg_logo, "5", "Five"))
arrayList.add(NumbersView(R.drawable.gfg_logo, "6", "Six"))
arrayList.add(NumbersView(R.drawable.gfg_logo, "7", "Seven"))
arrayList.add(NumbersView(R.drawable.gfg_logo, "8", "Eight"))
arrayList.add(NumbersView(R.drawable.gfg_logo, "9", "Nine"))
arrayList.add(NumbersView(R.drawable.gfg_logo, "10", "Ten"))
arrayList.add(NumbersView(R.drawable.gfg_logo, "11", "Eleven"))
arrayList.add(NumbersView(R.drawable.gfg_logo, "12", "Twelve"))
arrayList.add(NumbersView(R.drawable.gfg_logo, "13", "Thirteen"))
arrayList.add(NumbersView(R.drawable.gfg_logo, "14", "Fourteen"))
arrayList.add(NumbersView(R.drawable.gfg_logo, "15", "Fifteen"))
// Now create the instance of the NumbersViewAdapter and pass
// the context and arrayList created above
val numbersArrayAdapter = NumbersViewAdapter(this, arrayList)
// create the instance of the ListView to set the numbersViewAdapter
val numbersListView = findViewById<ListView>(R.id.listView)
// set the numbersViewAdapter for ListView
numbersListView.adapter = numbersArrayAdapter
}
}