Android ListView Tutorial

1- What is ListView?

ListView is a  view group which displays elements according to a list and can be scrolled vertically. Listview is an important view and is used widely in Android applications.
A simple example of ListView is your contact book, where you have a list of your contacts displayed in a ListView.
Addition to ListView, Android also provides you another similar view is ExpandableListView

1.1- ListItem

An Android ListView is made from a group of ListItem(s). ListItems are individual rows in listview where the data will be displayed. Any data in listview is displayed only through listItem. Consider Listview as scrollable group of ListItems.
A Listitem is a piece of the interface which can be created by a number of View.
Android builds some several different ListItem forms, called the pre-defined layout, which will be mentioned in the examples of this document.

1.2- Adapter

Android Adapter is a bridge between the View (e.g. ListView) and the underlying data for that view. An adapter manages the data and adapts the data to the individual rows (listItems) of the view.

We bind the adapter with Android listview via setAdapter method. Now, Let us see how adapter works with the help of the following image.

AdapterView

There are many  View needed  Android Adapter to manage displayed data, these Views is subclass of  AdapterView, you can see it in the below illustration:

Android Adapter

1.3- ListView Selector

To make ListView become more beautiful, you need to customize the effects, such as changing the background color of ListItem when cursor moves over it, or change background color when it is selected. You can see an example for customizing ListView Selector in the end of this document.

2- Basic ListView using ArrayAdapter

2.1- ArrayAdapter

ArrayAdapter used to display the ListView  with simple ListItem, ListItem can be made from only one TextView, CheckedTextView, EditText, ...

In case where you want to have a ListView with more complex  ListItem, you can manually create a customized Adapter.

2.2- ListView and ArrayAdapter example

Create New Project named SimpleListView.
  • activity_main.xml
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:paddingBottom="@dimen/activity_vertical_margin"
   android:paddingLeft="@dimen/activity_horizontal_margin"
   android:paddingRight="@dimen/activity_horizontal_margin"
   android:paddingTop="@dimen/activity_vertical_margin"
   tools:context="org.o7planning.simplelistview.MainActivity">

   <ListView
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:id="@+id/listView"
       android:layout_alignParentTop="true"
       android:layout_centerHorizontal="true" />
   
</RelativeLayout>
UserAccount.java
package org.o7planning.simplelistview;

import java.io.Serializable;

public class UserAccount implements Serializable {

   private String userName;
   private String userType;

   private boolean active;

   public UserAccount(String userName, String userType)  {
       this.userName= userName;
       this.userType = userType;
       this.active= true;
   }

   public UserAccount(String userName, String userType, boolean active)  {
       this.userName= userName;
       this.userType = userType;
       this.active= active;
   }

   public String getUserType() {
       return userType;
   }

   public void setUserType(String userType) {
       this.userType = userType;
   }

   public String getUserName() {
       return userName;
   }

   public void setUserName(String userName) {
       this.userName = userName;
   }

   public boolean isActive() {
       return active;
   }

   public void setActive(boolean active) {
       this.active = active;
   }

   @Override
   public String toString() {
       return this.userName +" ("+ this.userType+")";
   }

}
MainActivity.java
package org.o7planning.simplelistview;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class MainActivity extends AppCompatActivity {

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);

       ListView listView = (ListView)findViewById(R.id.listView);

       //

       UserAccount tom = new UserAccount("Tom","admin");
       UserAccount jerry = new UserAccount("Jerry","user");
       UserAccount donald = new UserAccount("Donald","guest", false);

       UserAccount[] users = new UserAccount[]{tom,jerry, donald};


       // android.R.layout.simple_list_item_1 is a constant predefined layout of Android.
       // used to create a ListView with simple ListItem (Only one TextView).

       ArrayAdapter<UserAccount> arrayAdapter
               = new ArrayAdapter<UserAccount>(this, android.R.layout.simple_list_item_1 , users);


       listView.setAdapter(arrayAdapter);
   }


}
Running the example:

2.3- The layouts is available to work with ArrayAdapter

Android built some Layout (for Grid Item, List Item, ..) can work with ArrayAdapter.

android.R.layout.simple_list_item_1

  • This is a simple layout of ListItem, created by single TextView (You can see examples above).

android.R.layout.simple_list_item_checked
   &
android.R.layout.simple_list_item_multiple_choice

  • Two Layout above are the simple layouts to create a ListView with ListItem created by single checkbox.
You can see this example here:

3- Customizing ListView using BaseAdapter

You can customize a ListView. Your Adapter should extend from BaseAdapter class.

3.1- Custom ListView example

Create new project named CustomListView.
Preview application:
Firstly you need to prepare some images:
Copy and paste the image file into the mipmap folder:
  1. vi.png 
  2. us.png
  3. ru.png
You need to create a layout for listitem. In Android Studio right-click the res/layout and selecte:
  • New/Layout resource file
Enter:
  • File name: list_item_layout.xml
  • Root element: RelativeLayout
Interface Design for List-Item.
Slider, interface design steps for List Item:
  • SLIDER
ImageView
  • ID: imageView_flag

TextView 1:
  • ID: textView_countryName
  • Text: Country Name

TextView 2:
  • ID: textView_population
  • Text: Population ....
list_item_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="match_parent">

    <ImageView
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:id="@+id/imageView_flag"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_marginLeft="5dp"
        android:layout_marginRight="5dp"
        android:layout_marginTop="5dp" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:text="Country Name"
        android:id="@+id/textView_countryName"
        android:layout_alignParentTop="true"
        android:layout_toRightOf="@+id/imageView_flag"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true"
        android:layout_above="@+id/textView_population"
        android:layout_margin="5dp" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceSmall"
        android:text="Population: ..."
        android:id="@+id/textView_population"
        android:layout_alignBottom="@+id/imageView_flag"
        android:layout_alignLeft="@+id/textView_countryName"
        android:layout_alignStart="@+id/textView_countryName"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true"
        android:layout_margin="5dp" />

</RelativeLayout>
Country.java
package org.o7planning.countrieslistview;

package org.o7planning.customlistview;



public class Country {

   private String countryName;

   // Image name (Without extension)
   private String flagName;
   private int population;

   public Country(String countryName, String flagName, int population) {
       this.countryName= countryName;
       this.flagName= flagName;
       this.population= population;
   }

   public int getPopulation() {
       return population;
   }

   public void setPopulation(int population) {
       this.population = population;
   }

   public String getCountryName() {
       return countryName;
   }

   public void setCountryName(String countryName) {
       this.countryName = countryName;
   }

   public String getFlagName() {
       return flagName;
   }

   public void setFlagName(String flagName) {
       this.flagName = flagName;
   }

   @Override
   public String toString()  {
       return this.countryName+" (Population: "+ this.population+")";
   }
}
  • activity_main.xml
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:paddingBottom="@dimen/activity_vertical_margin"
   android:paddingLeft="@dimen/activity_horizontal_margin"
   android:paddingRight="@dimen/activity_horizontal_margin"
   android:paddingTop="@dimen/activity_vertical_margin"
   tools:context="org.o7planning.customlistview.MainActivity">

   <ListView
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:id="@+id/listView"
       android:layout_alignParentTop="true"
       android:layout_centerHorizontal="true" />
   
</RelativeLayout>
CustomListAdapter is a class extending from BaseAdapter, used to display data on the List-Item.
CustomListAdapter.java
package org.o7planning.customlistview;


import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;

public class CustomListAdapter  extends BaseAdapter {

   private List<Country> listData;
   private LayoutInflater layoutInflater;
   private Context context;

   public CustomListAdapter(Context aContext,  List<Country> listData) {
       this.context = aContext;
       this.listData = listData;
       layoutInflater = LayoutInflater.from(aContext);
   }

   @Override
   public int getCount() {
       return listData.size();
   }

   @Override
   public Object getItem(int position) {
       return listData.get(position);
   }

   @Override
   public long getItemId(int position) {
       return position;
   }

   public View getView(int position, View convertView, ViewGroup parent) {
       ViewHolder holder;
       if (convertView == null) {
           convertView = layoutInflater.inflate(R.layout.list_item_layout, null);
           holder = new ViewHolder();
           holder.flagView = (ImageView) convertView.findViewById(R.id.imageView_flag);
           holder.countryNameView = (TextView) convertView.findViewById(R.id.textView_countryName);
           holder.populationView = (TextView) convertView.findViewById(R.id.textView_population);
           convertView.setTag(holder);
       } else {
           holder = (ViewHolder) convertView.getTag();
       }

       Country country = this.listData.get(position);
       holder.countryNameView.setText(country.getCountryName());
       holder.populationView.setText("Population: " + country.getPopulation());

       int imageId = this.getMipmapResIdByName(country.getFlagName());

       holder.flagView.setImageResource(imageId);

       return convertView;
   }

   // Find Image ID corresponding to the name of the image (in the directory mipmap).
   public int getMipmapResIdByName(String resName)  {
       String pkgName = context.getPackageName();
       // Return 0 if not found.
       int resID = context.getResources().getIdentifier(resName , "mipmap", pkgName);
       Log.i("CustomListView", "Res Name: "+ resName+"==> Res ID = "+ resID);
       return resID;
   }

   static class ViewHolder {
       ImageView flagView;
       TextView countryNameView;
       TextView populationView;
   }

}
ActivityMain.java
package org.o7planning.customlistview;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;


import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);


       List<Country> image_details = getListData();
       final ListView listView = (ListView) findViewById(R.id.listView);
       listView.setAdapter(new CustomListAdapter(this, image_details));

       // When the user clicks on the ListItem
       listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {

           @Override
           public void onItemClick(AdapterView<?> a, View v, int position, long id) {
               Object o = listView.getItemAtPosition(position);
               Country country = (Country) o;
               Toast.makeText(MainActivity.this, "Selected :" + " " + country, Toast.LENGTH_LONG).show();
           }
       });
   }

   private  List<Country> getListData() {
       List<Country> list = new ArrayList<Country>();
       Country vietnam = new Country("Vietnam", "vn", 98000000);
       Country usa = new Country("United States", "us", 320000000);
       Country russia = new Country("Russia", "ru", 142000000);


       list.add(vietnam);
       list.add(usa);
       list.add(russia);

       return list;
   }

}
Running the example:

3.2- Custom Selector example

To make ListView become more beautiful, you need to customize the effects, such as changing the background color of ListItem when cursor moves over it, or change background color when it is selected. We continue with the example above.
Create the configuration files:
  • File name: list_item_normal.xml
  • Directory: drawable
Similarly create three different files:
  • item_state_pressed.xml
  • item_state_selected.xml
  • list_selector.xml
When the list item in a normal state, styles which is set up  in item_state_normal.xml will be applied to the ListItem.
item_state_normal.xml
<?xml version="1.0" encoding="utf-8"?>

<shape xmlns:android="http://schemas.android.com/apk/res/android"
  android:shape="rectangle">

  <gradient
      android:startColor="#f1f1f2"
      android:centerColor="#e7e7e8"
      android:endColor="#cfcfcf"
      android:angle="270" />

</shape>
When List Item is pressed, the styles set up in item_state_pressed.xml  will be applied to the ListItem
item_state_pressed.xml
<?xml version="1.0" encoding="utf-8"?>

<shape xmlns:android="http://schemas.android.com/apk/res/android"
  android:shape="rectangle">

  <gradient
      android:startColor="#18d7e5"
      android:centerColor="#16cedb"
      android:endColor="#09adb9"
      android:angle="270" />

</shape>
When List Item is selected, the styles set up in item_state_selected.xml  will be applied to the ListItem
 
item_state_selected.xml
<?xml version="1.0" encoding="utf-8"?>

<shape xmlns:android="http://schemas.android.com/apk/res/android"
  android:shape="rectangle">

  <gradient
      android:startColor="#18d7e5"
      android:centerColor="#16cedb"
      android:endColor="#09adb9"
      android:angle="270" />

</shape>
Mapping the specific status of the ListItem with xml files.
list_selector.xml
<?xml version="1.0" encoding="utf-8"?>

<selector xmlns:android="http://schemas.android.com/apk/res/android">

       <item
           android:state_selected="false"
           android:state_pressed="false"
           android:drawable="@drawable/item_state_normal" />

       <item android:state_pressed="true"
           android:drawable="@drawable/item_state_pressed" />

       <item android:state_selected="true"
           android:state_pressed="false"
           android:drawable="@drawable/item_state_selected" />


</selector>
Set ListSelector for ListView:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:paddingBottom="@dimen/activity_vertical_margin"
   android:paddingLeft="@dimen/activity_horizontal_margin"
   android:paddingRight="@dimen/activity_horizontal_margin"
   android:paddingTop="@dimen/activity_vertical_margin"
   tools:context="org.o7planning.customlistview.MainActivity">

   <ListView
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:id="@+id/listView"
       android:layout_alignParentTop="true"
       android:layout_centerHorizontal="true"
       android:listSelector="@drawable/list_selector" />

</RelativeLayout>
Rerun you application: