One of the UI components we use often is Android ListView. This tutorial describes how to use this Android UI component and how to use it in the right way.
One interesting aspect is this component can be deeply customized and can be adapted to our needs. This post wants to analyze the basic concepts behind it and how to use this component.

In this tutorial you will learn:

  • How to use Android ListView in an Android UI Interface
  • How to use an Android Custom Adapter to customize the Android ListView behaviour
  • How to create ContextMenu
  • How to customize the Android ListView

In order to show items inside the list, this component uses an adapter, so if we want to populate the list we have first create or use existing adapters. Android SDK has some standard adapters that are useful in many cases.
To understand how we can use this UI component we can suppose we want to show a list of planets. To make things simple and focus our attention on the ListView component we will use, by now, a very simple and basic adapter called SimpleAdapter.

Android ListView tutorial: getting started


Android ListView: Tutorial – getting started

So let’s create an android project to show how to use Android Listview ArrayAdapter. In this project, create an activity called MainActivity and a layout called activity_main.xml. This layout is very simple, it just contains the listView component.

<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">
   <ListView android:id="@+id/listView"
             android:layout_height="match_parent" 
             android:layout_width="match_parent"/>
</RelativeLayout>

Our MainActivity class is:

public class MainActivity extends Activity {

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

 

How to use Android ListView with ArrayAdapter

Now in our Android ListView tutorial, it is the time to populate our listView using Android Listview ArrayAdapter. As said before we use a SimpleAdapter, a standard adapter present in SDK. Let’s suppose we want to show an Android Listview with ArrayAdapter with the solar system planets. SimpleAdapter accepts a java.util.List with element’s type of java.util.Map. In our case we have then:

// The data to show
List<Map<String, String>> planetsList = new ArrayList<Map<String,String>>();
.....

private void initList() {
  // We populate the planets
  planetsList.add(createPlanet("planet", "Mercury"));
  planetsList.add(createPlanet("planet", "Venus"));
  planetsList.add(createPlanet("planet", "Mars"));
  planetsList.add(createPlanet("planet", "Jupiter"));
  planetsList.add(createPlanet("planet", "Saturn"));
  planetsList.add(createPlanet("planet", "Uranus"));
  planetsList.add(createPlanet("planet", "Neptune"));
}

private HashMap<String, String> createPlanet(String key, String name) {
  HashMap<String, String> planet = new HashMap<String, String>();
  planet.put(key, name);
  return planet;
}

and the SimpleAdapter can be instantiated as:

// This is a simple adapter that accepts as parameter
// Context
// Data list
// The row layout that is used during the row creation
// The keys used to retrieve the data
// The View id used to show the data. The key number and the view id must match
simpleAdpt = new SimpleAdapter(this, planetsList, android.R.layout.simple_list_item_1, 
                              new String[] {"planet"}, new int[] {android.R.id.text1});

where the first parameter is the context reference (our Activity), the second the data we want so show in the list. The 3rd is the layout we want to use for each row in the list. In this case we just used a pre-built layout shipped with Android SDK that you can find inside the android sdk directory and then  platformsandroid-16datareslayout. This is a very simple layout that contains just a TextView with id text1. The 4th parameter is an array of key used to retrieve the data from the Map. Each list element of java.util.List represents a row in the ListView and each element inside the row must have a unique key that is used to retrieve the element content. In our case to make things very simple we just used planet as key. The 5th element is an array of int representing the ids of the View inside the row layout. In our case is just text1 id. Please notice that the keys array length must match the ids array length.

 We have almost done. Let’s modify the onCreate method in our Activity like that:
@Override
public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  initList();

  // We get the ListView component from the layout
  ListView lv = (ListView) findViewById(R.id.listView);

  // This is a simple adapter that accepts as parameter
  // Context
  // Data list
  // The row layout that is used during the row creation
  // The keys used to retrieve the data
  // The View id used to show the data. The key number and the view id must match
  simpleAdpt = new SimpleAdapter(this, planetsList, android.R.layout.simple_list_item_1, 
              new String[] {"planet"}, new int[] {android.R.id.text1});

  lv.setAdapter(simpleAdpt);
}

Running the app we get:

Android ListView
Android ListView User interaction

Once we have created our list and populated it with the items we want to interact with the user giving the chance to click one item or maybe show a context menu. To do it we have to register some listener.

If we want to listen when the user clicks on an item we simply have to implement the AdapterView.OnItemClickListener(). So we have:

// React to user clicks on item
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {

public void onItemClick(AdapterView<?>, parentAdapter, 
                        View view, int position,
                        long id) {

   // We know the View is a TextView so we can cast it
   TextView clickedView = (TextView) view;

   Toast.makeText(MainActivity.this, "Item with id ["+id+"] - 
              Position ["+position+"] - Planet ["+clickedView.getText()+"]",
              Toast.LENGTH_SHORT).show();

  }
});

When the user clicks on an item we simply show the position and the id of the item clicked using a simple Toast.

What about if we want to create a context menu when a user long click on an item? Well, this is very simple because we have to override the onCreateContextMenu method in the Activity class and register the class as a listener. First, override the method:

// We want to create a context Menu when the user long click on an item
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
            ContextMenuInfo menuInfo) {

  super.onCreateContextMenu(menu, v, menuInfo);
  AdapterContextMenuInfo aInfo = (AdapterContextMenuInfo) menuInfo;

  // We know that each row in the adapter is a Map
  HashMap map = (HashMap) simpleAdpt.getItem(aInfo.position);

  menu.setHeaderTitle("Options for " + map.get("planet"));
  menu.add(1, 1, 1, "Details");
  menu.add(1, 2, 2, "Delete");
}

What do we do with this method? Well, first we call super to let the SO makes its work. Then we cast the ContextMenuInfo to AdapterContextMenuInfo because we are using a ListView. The AdapterContextMenuInfo has an attribute that holds the item position clicked so we use this information to retrieve the item information. We know we are using an HashMap to represent the row so we cast the result to HashMap. It is the time we create the menu.

First, we create the menu header using the name of the planet retrieved using the HashMap and then set two options “Details” and “Delete” with different ids but belonging to the same group we called “1”.

Before running our project we have to modify onCreate method, to register our MainClass as the handler for the context menu for the ListView.

// we register for the contextmneu
registerForContextMenu(lv);

Let’s run our project and when we long click on an item we will get:

Android ListView ContextMenu

The last step handles when a user clicks on one of the options. We have to override the method onContextItemSelected like that:

// This method is called when user selects an Item in the Context menu
@Override
public boolean onContextItemSelected(MenuItem item) {
  int itemId = item.getItemId();
  // Implements our logic
  Toast.makeText(this, "Item id ["+itemId+"]", Toast.LENGTH_SHORT).show();
  return true;
}

In this case, we simply show a Toast with the menu item id.

How to customize the Android ListView

One aspect we didn’t consider is how to apply a style to Android list view background. We can customize the look of the Android ListView background in the way we like, for example with can use as background rounded corner, alternate color and so on. By now we considered just custom adapter without taking into account how we can customize how each item inside the listview appears.

We want to describe how we can use a resource to customize the item look. The first example will describe how we can create rounded corners for each item inside a listview. In the second example, we will show how we can alternate the background color.

android listview background

Android ListView background with rounded corner

Let’s suppose we want to create an Android ListView background with a rounded corner for each item. How can we do this?…We need to create some drawable resources and apply them to each item. As you already know we have to create a custom adapter to implement this behavior.
As we said the first thing we need is a drawable resource. As you may already know this is a powerful feature of Android because it permits us to create a geometrical figure in XML style. We have to specify some information to create this figure:

  • border size and color
  • background color (in our case a solid color)
  • corners

We need to create a file XML under the res/drawable directory. Let’s call this file rounded_corners.xml. This file contains a shape definition. A shape is a geometrical figure that is described by other tags:

  • stroke – a stroke line for the shape (witdh, color, dashWidth and dashGap)
  • solid – solid colour that fills the shape
  • corners – radius and so o

So the rounded_corners.xml look like:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
   <solid android:color="#00FF00"/>
   <corners android:radius="5dp"/>
   <padding android:left="3dp" android:top="3dp" android:right="3dp" android:bottom="3dp"/>
   <stroke android:width="3dp" android:color="#00CC00"/>
</shape>

Once we have created our shape we need to apply it to the items. To do it we have to create another XML file that describes how we apply this shape. In this case, we use the XML tag selector to specify when and how to apply the shape. To specify when to apply the shape we use the status. We specify to apply this shape when:

  • status = enable
  • status = selected
  • status = pressed

So our file (listview_selector.xml) looks like:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:drawable="@drawable/rounded_corner" android:state_enabled="true"/>
<item android:drawable="@drawable/rounded_corner" android:state_pressed="true"/>
<item android:drawable="@drawable/rounded_corner" android:state_focused="true"/>
</selector>

Now we have defined our resource, we simply need to specify to apply it in our adapter in this way:

public View getView(int position, View convertView, ViewGroup parent) { 
   View v = convertView; PlanetHolder holder = new PlanetHolder(); 
   // First let's verify the convertView is not null 
   if (convertView == null) { 
      // This a new view we inflate the new layout 
      LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
      v = inflater.inflate(R.layout.row_layout, null); 
      // Now we can fill the layout with the right values 
      TextView tv = (TextView) v.findViewById(R.id.name); 
      holder.planetNameView = tv; v.setTag(holder); 
      v.setBackgroundResource(R.drawable.rounded_corner); 
  } 
  else 
   holder = (PlanetHolder) v.getTag(); 

  Planet p = planetList.get(position);
  holder.planetNameView.setText(p.getName()); 
  return v; 
}

If we run the app we have: How to customize Android ListView How to customize Android ListView row with alternate color As we describe above, if we want to change how each row look like inside the ListView we have simply change the resource and we can customize its look. For example we can suppose we want to alternate the row color. In this case we need to create two drawable resource one for each background, like that: even_row.xml

<xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <solid android:color="#A0A0A0"/> <padding android:left="3dp" android:top="3dp" android:right="3dp" android:bottom="3dp" /> <stroke android:width="1dp" android:color="#00CC00"/> </shape>

odd_row.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
  <solid android:color="#F0F0F0"/>
  <padding android:left="3dp" android:top="3dp" android:right="3dp" android:bottom="3dp"/>

  <stroke android:width="1dp" android:color="#00CC00"/>
</shape>

We need moreover two selectors that use the drawable resources, like that

listview_selector_even.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:drawable="@drawable/even_row" android:state_enabled="true"/>
  <item android:drawable="@drawable/even_row" android:state_pressed="true"/>
  <item android:drawable="@drawable/even_row" android:state_focused="true"/>
</selector>

listview_selector_odd.xml

<?xml version="1.0" encoding="utf-8"?>
  <selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:drawable="@drawable/odd_row" android:state_enabled="true"/>
  <item android:drawable="@drawable/odd_row" android:state_pressed="true"/>
  <item android:drawable="@drawable/odd_row" android:state_focused="true"/>
</selector>

And finally we apply them inside our custom adapter:

public View getView(int position, View convertView, ViewGroup parent) {
  View v = convertView;
  PlanetHolder holder = new PlanetHolder();

  // First let's verify the convertView is not null
  if (convertView == null) {
    // This a new view we inflate the new layout
    LayoutInflater inflater = (LayoutInflater) 
       context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    v = inflater.inflate(R.layout.row_layout, null);
    // Now we can fill the layout with the right values
    TextView tv = (TextView) v.findViewById(R.id.name);

    holder.planetNameView = tv;
    v.setTag(holder);
    if ( position % 2 == 0)
       v.setBackgroundResource(R.drawable.listview_selector_even);
   else
    v.setBackgroundResource(R.drawable.listview_selector_odd);
   }
   else
     holder = (PlanetHolder) v.getTag();

   Planet p = planetList.get(position);
   holder.planetNameView.setText(p.getName());

   return v;
}

Running the app we have:

Android ListView with custom row style

As you can notice the Android ListView background is customized using our style.

32 COMMENTS

  1. Great tutorial. However, in order to get the correct ID to display after one of the context menu items was added I had to modify the code as follows:

    // This method is called when user selects an Item in the Context menu
    @Override
    public boolean onContextItemSelected(MenuItem item) {
    AdapterContextMenuInfo itemInfo = (AdapterContextMenuInfo) item.getMenuInfo();
    int itemId = itemInfo.position;
    // Implements our logic
    Toast.makeText(this, "Item id ["+itemId+"]", Toast.LENGTH_SHORT).show();
    return true;
    }

  2. while running the listview code mentioned above, i get a message – 'unfortunately your app is closed'. Can you please advice what is the possible reason for this ? Thanks.

  3. I can’t follow the tuts here

    I see the codes like this, and it confuses me.
    List<Map<String, String>> planetsList = new ArrayList<Map<String

LEAVE A REPLY

Please enter your comment!
Please enter your name here