Customizing Android ListView Items with Custom ArrayAdapter

You are currently viewing Customizing Android ListView Items with Custom ArrayAdapter

For real-world commercial mobile applications, the default look and feel of the Android ListView is not very attractive. It only renders a simple String in every ListView row using the internal TextView control. For most applications, you want to create an interface that is more graphically rich and visually pleasing to the user. Luckily, ListView is very powerful control and with the help of custom item layouts, it can be customized to suit your needs easily. In this tutorial, I will show you how you can create custom ListView items with icons, custom header layout and how you can use custom ArrayAdapter to glue everything together. I will also show you some performance improvement tips you can use to optimize the memory usage of your ListView control.

Create a new Android project in Eclipse with MainActivity as your default Activity and main.xml as your default layout file. Declare a ListView control in your main.xml layout file as shown in the following code.

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" 
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#FFFFFF"> 
    
     <ListView
        android:id="@+id/listView1"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />
     
</LinearLayout>

The above code is using simple LinearLayout with vertical orientation, and a ListView is declared to cover the entire width and height of the parent container using the fill_parent as the value of both android:layout_height and android:layout:width properties. ListView also has a unique id listView1 that will be used in the MainActivity to reference the ListView control.

To create the custom header for the ListView, create a new xml layout file listview_header_row.xml in the layout folder of your project and declare a TextView control in it with the properties shown in the following code. This will create a blue header with the white color text showing Weather Photos.

READ ALSO:  Binding Android ListView with Custom Objects using ArrayAdapter

listview_header_row.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" 
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"> 
        
     <TextView android:id="@+id/txtHeader"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:gravity="center_vertical"
        android:layout_alignParentTop="true"
        android:layout_alignParentBottom="true"
        android:textStyle="bold"
        android:textSize="22dp"
        android:textColor="#FFFFFF"
        android:padding="10dp"
        android:text="Weather Photos"
        android:background="#336699" />

</LinearLayout>

To create a custom ListView row, create another xml layout file listview_item_row.xml in the project layout directory. Android will render this file content in every ListView item and you are free to declare any control you want in it. For this tutorial I am using an ImageView for an icon and a TextView for displaying items titles. Following is the code for listview_item_row.xml file.

listview_item_row.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" 
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:padding="10dp">
    
     <ImageView android:id="@+id/imgIcon"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:gravity="center_vertical"
        android:layout_alignParentTop="true"
        android:layout_alignParentBottom="true"
        android:layout_marginRight="15dp"
        android:layout_marginTop="5dp"
        android:layout_marginBottom="5dp" />
        
     <TextView android:id="@+id/txtTitle"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:gravity="center_vertical"
        android:layout_alignParentTop="true"
        android:layout_alignParentBottom="true"
        android:textStyle="bold"
        android:textSize="22dp"
        android:textColor="#000000"
        android:layout_marginTop="5dp"
        android:layout_marginBottom="5dp" />
        
</LinearLayout>

For this tutorial, I have downloaded some 32 x 32 pixels icons in PNG format. You can use your own icons if you want. Once your icons are ready, drag the icons from your folder to the project drawable-mdpi directory. Next create a new Java class in your project and named it Weather.java. This class will be used to create a custom ArrayAdapter and to bind the objects with the ListView later in this tutorial. Following is the code of Weather.java class. It has two simple properties icon and title and a typical class constructor to initialize the properties.

Weather.java

public class Weather {
    public int icon;
    public String title;
    public Weather(){
        super();
    }
    
    public Weather(int icon, String title) {
        super();
        this.icon = icon;
        this.title = title;
    }
}

Notice that the above listview_item_row.xml file has two views, which are correspondent to the properties of the Weather class. The values of the Weather class properties will be displayed on those views and to connect these two pieces together you need to create a custom ArrayAdapter that will inherit the Android ArrayAdapter class and will override the getView method. Add a new java class in your project with the name WeatherAdapter and implement the code as shown below:

READ ALSO:  Display Data using new ListView Control in ASP.NET

WeatherAdapter.java

public class WeatherAdapter extends ArrayAdapter<Weather>{

    Context context; 
    int layoutResourceId;    
    Weather data[] = null;
    
    public WeatherAdapter(Context context, int layoutResourceId, Weather[] data) {
        super(context, layoutResourceId, data);
        this.layoutResourceId = layoutResourceId;
        this.context = context;
        this.data = data;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View row = convertView;
        WeatherHolder holder = null;
        
        if(row == null)
        {
            LayoutInflater inflater = ((Activity)context).getLayoutInflater();
            row = inflater.inflate(layoutResourceId, parent, false);
            
            holder = new WeatherHolder();
            holder.imgIcon = (ImageView)row.findViewById(R.id.imgIcon);
            holder.txtTitle = (TextView)row.findViewById(R.id.txtTitle);
            
            row.setTag(holder);
        }
        else
        {
            holder = (WeatherHolder)row.getTag();
        }
        
        Weather weather = data[position];
        holder.txtTitle.setText(weather.title);
        holder.imgIcon.setImageResource(weather.icon);
        
        return row;
    }
    
    static class WeatherHolder
    {
        ImageView imgIcon;
        TextView txtTitle;
    }
}

In the above code, the first important thing is the constructor of the class that takes three parameters. The first parameter is the Context and we can pass the reference of the activity in which we will use WeatherAdapter class. The second parameter is the resource id of the layout file we want to use for displaying each ListView item. We will pass the resource id of our layout file listview_item_row.xml for this parameter. The third parameter is an array of Weather class objects that will be used by the Adapter to display data.

Next the parent class getView method is overridden. This method will be called for every item in the ListView to create views with their properties set as we want. The getView method is also using a temporary holder class declared inside the WeatherAdapter class. This class will be used to cache the ImageView and TextView so they can be reused for every row in the ListView and this will provide us a great performance improvement as we are recycling the same two views with different properties and we don’t need to find ImageView and TextView for every ListView item. The above code is also using the Android built in Layout Inflator to inflate (parse) the xml layout file.

READ ALSO:  Building a Food Recipes Mobile App with Ionic 3x and Angular 5 - Part 5

The final piece of code is our application MainActivity in which we will be using all the above objects we declared. Following is the code of the MainActivity.java file.

MainActivity.java

public class MainActivity extends Activity {

    private ListView listView1;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        Weather weather_data[] = new Weather[]
        {
            new Weather(R.drawable.weather_cloudy, "Cloudy"),
            new Weather(R.drawable.weather_showers, "Showers"),
            new Weather(R.drawable.weather_snow, "Snow"),
            new Weather(R.drawable.weather_storm, "Storm"),
            new Weather(R.drawable.weather_sunny, "Sunny")
        };
        
        WeatherAdapter adapter = new WeatherAdapter(this, 
                R.layout.listview_item_row, weather_data);
        
        
        listView1 = (ListView)findViewById(R.id.listView1);
         
        View header = (View)getLayoutInflater().inflate(R.layout.listview_header_row, null);
        listView1.addHeaderView(header);
        
        listView1.setAdapter(adapter);
    }
}

There are few things in the MainActivity that required explanation for your better understanding. First notice, we are creating an array of Weather class objects and icons and titles are passed as constructor parameters. Next, WeatherAdapter object is created and listview_item_row.xml layout file resource id and Weather objects array is passed in its constructor. Once again, we are using Android Layout Inflator to inflate our listview_header_row.xml layout file and once it is parses as header View it is passed as a parameter of ListView addHeaderView method. Finally we are passing our custom adapter to ListView setAdapter method. That’s about it. You are ready to build and run our project. If everything implemented properly you will see the following output.

Leave a Reply