Android Series: Custom ListView items and adapters
This is a short tutorial on how to populate your android list view, with data downloaded from the internet or other sources, using ArrayAdapter. ListView items view is declared in a separate XML file and displayed using custom adapter class.
First things first, so go ahead and create a new project using Eclipse equipped with ADT plugin.
The project described below assumes you have a list of objects created, this can be either downloaded from the internet as XML and parsed to create ArrayList of your custom objects or anything you imagine. I will not go into details on this tutorial how to create such an ArrayList but your imagination is the limit. Parsing XML downloaded from the net will be covered in the next tutorial coming up soon.
Click File -> New -> Project and select the ‘Android Project’ wizard:
Click next and fill out the next screen with the following values:
Once you have filled out all the necessary data you can click finish.
Your new project has just been created. Now lets modify it a bit to display our custom made list.
Open up SoftwarePassionView.java in the eclipse editor and change the class file to the following:
1. Define necessary member variables we will use later in our class
1 2 3 4 | private ProgressDialog m_ProgressDialog = null; private ArrayList<Order> m_orders = null; private OrderAdapter m_adapter; private Runnable viewOrders; |
m_ProgressDialog is a small pop up displaying information that your data is being downloaded or retrieved other way.
m_orders is an ArrayList which will hold our data downloaded from the internet or acquired other way
m_adapter is our custom class extending ArrayAdapter
viewOrders is a runnable for downloading data from the internet in a separate thread
To import whatever you can at this point click Ctrl+Shift+O, some classes like Order or OrderAdapter are not created yet but don’t worry we will come to that point soon.
Another important note at this point is that your SoftwarePassoinView should extend ListActivity instead of simple Activity.
Your class should look more or less something like this now:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | package com.softberries.lve; import java.util.ArrayList; import android.app.ListActivity; import android.app.ProgressDialog; import android.os.Bundle; public class SoftwarePassionView extends ListActivity{ private ProgressDialog m_ProgressDialog = null; private ArrayList<Order> m_orders = null; private OrderAdapter m_adapter; private Runnable viewOrders; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } } |
Now lets create our simple Order class holding single order.
Right click on the project and and select ‘New’ -> ‘Class’, name it order and open it up in the editor.
The source code for our orders looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | package com.softberries.lve; public class Order { private String orderName; private String orderStatus; public String getOrderName() { return orderName; } public void setOrderName(String orderName) { this.orderName = orderName; } public String getOrderStatus() { return orderStatus; } public void setOrderStatus(String orderStatus) { this.orderStatus = orderStatus; } } |
The Order class is very simple and contains only 2 strings with getters and setter generated for them
Now lets change our main.xml file to hold our custom list items:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <?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" > <ListView android:id="@+id/android:list" android:layout_width="fill_parent" android:layout_height="fill_parent" /> <TextView android:id="@+id/android:empty" android:layout_width="fill_parent" android:layout_height="fill_parent" android:text="@string/main_no_items"/> </LinearLayout> |
This layout will display our list items if any and if the list is empty it will display ‘No orders to display’ string defined in string.xml resource file.
1 2 3 4 5 6 | <?xml version="1.0" encoding="utf-8"?> <resources> <string name="hello">Hello World, SoftwarePassionView!</string> <string name="app_name">Software Passion</string> <string name="main_no_items">No orders to display</string> </resources> |
Our list item (single row on the list) have a custom layout as well, defined in row.xml file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="?android:attr/listPreferredItemHeight" android:padding="6dip"> <ImageView android:id="@+id/icon" android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_marginRight="6dip" android:src="@drawable/icon" /> <LinearLayout android:orientation="vertical" android:layout_width="0dip" android:layout_weight="1" android:layout_height="fill_parent"> <TextView android:id="@+id/toptext" android:layout_width="fill_parent" android:layout_height="0dip" android:layout_weight="1" android:gravity="center_vertical" /> <TextView android:layout_width="fill_parent" android:layout_height="0dip" android:layout_weight="1" android:id="@+id/bottomtext" android:singleLine="true" android:ellipsize="marquee" /> </LinearLayout> </LinearLayout> |
Single row example has been borrowed from the romain Guy website here
Ok, so we have all our layouts defined in the res folder under layout. Now its time to go back to our code and create our custom OrderAdapter class which will manage our list of orders:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | private class OrderAdapter extends ArrayAdapter<Order> { private ArrayList<Order> items; public OrderAdapter(Context context, int textViewResourceId, ArrayList<Order> items) { super(context, textViewResourceId, items); this.items = items; } @Override public View getView(int position, View convertView, ViewGroup parent) { View v = convertView; if (v == null) { LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); v = vi.inflate(R.layout.row, null); } Order o = items.get(position); if (o != null) { TextView tt = (TextView) v.findViewById(R.id.toptext); TextView bt = (TextView) v.findViewById(R.id.bottomtext); if (tt != null) { tt.setText("Name: "+o.getOrderName()); } if(bt != null){ bt.setText("Status: "+ o.getOrderStatus()); } } return v; } } |
This is a private class and should be added to our SoftwarePassionView. This is extended ListAdapter which inside overriden getView method returns our row with assigned string values to the textfields defined in row.xml.
A huge part of our application is already done. Now we have to add some modifications to the onCreate method to initialize everything properly and add a method retrieving our orders from somewhere, lets start with the latter:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | private void getOrders(){ try{ m_orders = new ArrayList<Order>(); Order o1 = new Order(); o1.setOrderName("SF services"); o1.setOrderStatus("Pending"); Order o2 = new Order(); o2.setOrderName("SF Advertisement"); o2.setOrderStatus("Completed"); m_orders.add(o1); m_orders.add(o2); Thread.sleep(2000); Log.i("ARRAY", ""+ m_orders.size()); } catch (Exception e) { Log.e("BACKGROUND_PROC", e.getMessage()); } runOnUiThread(returnRes); } |
Instead of creating our simple orders in the method above you could of course download them from somewhere and assign the result to the m_orders array list. The method runOnUIThread is a utility method for running tasks back on the main UI thread after the job is done on the separate thread created for long running tasks. We will call our getOrders method from a separate thread.
The returnRes runnable adds newly retrieved Order object to our custom Adapter and notifies it of the data change:
1 2 3 4 5 6 7 8 9 10 11 12 13 | private Runnable returnRes = new Runnable() { @Override public void run() { if(m_orders != null && m_orders.size() > 0){ m_adapter.notifyDataSetChanged(); for(int i=0;i<m_orders.size();i++) m_adapter.add(m_orders.get(i)); } m_ProgressDialog.dismiss(); m_adapter.notifyDataSetChanged(); } }; |
Now lets move to our overriden onCreate method. We will initialize here all the member variables as well as start a new thread retrieving our orders:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); m_orders = new ArrayList<Order>(); this.m_adapter = new OrderAdapter(this, R.layout.row, m_orders); setListAdapter(this.m_adapter); viewOrders = new Runnable(){ @Override public void run() { getOrders(); } }; Thread thread = new Thread(null, viewOrders, "MagentoBackground"); thread.start(); m_ProgressDialog = ProgressDialog.show(SoftwarePassionView.this, "Please wait...", "Retrieving data ...", true); } |
After initialization, we start new thread using the viewOrders runnable and show the progress dialog which we close once the orders are retrieved.
Now you should be able to run your application. After the application starts it spawns new thread and displays the loader:
![]() |
![]() |
And thats it. You can add an Item Click Listener to your list to start new activities etc.
Full source code for the SoftwarePassionView below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | package com.softberries.lve; import java.util.ArrayList; import android.app.ListActivity; import android.app.ProgressDialog; import android.content.Context; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.TextView; public class SoftwarePassionView extends ListActivity{ private ProgressDialog m_ProgressDialog = null; private ArrayList<Order> m_orders = null; private OrderAdapter m_adapter; private Runnable viewOrders; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); m_orders = new ArrayList<Order>(); this.m_adapter = new OrderAdapter(this, R.layout.row, m_orders); setListAdapter(this.m_adapter); viewOrders = new Runnable(){ @Override public void run() { getOrders(); } }; Thread thread = new Thread(null, viewOrders, "MagentoBackground"); thread.start(); m_ProgressDialog = ProgressDialog.show(SoftwarePassionView.this, "Please wait...", "Retrieving data ...", true); } private Runnable returnRes = new Runnable() { @Override public void run() { if(m_orders != null && m_orders.size() > 0){ m_adapter.notifyDataSetChanged(); for(int i=0;i<m_orders.size();i++) m_adapter.add(m_orders.get(i)); } m_ProgressDialog.dismiss(); m_adapter.notifyDataSetChanged(); } }; private void getOrders(){ try{ m_orders = new ArrayList<Order>(); Order o1 = new Order(); o1.setOrderName("SF services"); o1.setOrderStatus("Pending"); Order o2 = new Order(); o2.setOrderName("SF Advertisement"); o2.setOrderStatus("Completed"); m_orders.add(o1); m_orders.add(o2); Thread.sleep(5000); Log.i("ARRAY", ""+ m_orders.size()); } catch (Exception e) { Log.e("BACKGROUND_PROC", e.getMessage()); } runOnUiThread(returnRes); } private class OrderAdapter extends ArrayAdapter<Order> { private ArrayList<Order> items; public OrderAdapter(Context context, int textViewResourceId, ArrayList<Order> items) { super(context, textViewResourceId, items); this.items = items; } @Override public View getView(int position, View convertView, ViewGroup parent) { View v = convertView; if (v == null) { LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); v = vi.inflate(R.layout.row, null); } Order o = items.get(position); if (o != null) { TextView tt = (TextView) v.findViewById(R.id.toptext); TextView bt = (TextView) v.findViewById(R.id.bottomtext); if (tt != null) { tt.setText("Name: "+o.getOrderName()); } if(bt != null){ bt.setText("Status: "+ o.getOrderStatus()); } } return v; } } } |
Enjoy!




ABOVE IS SOLVED
You have to add this to the activity declaration in the manifest:
android:configChanges=”orientation”
So putting that in the configuration file avoids the system destroying your activity. Instead it invokes the onConfigurationChanged(Configuration) method.
http://stackoverflow.com/questions/1111980/how-to-handle-screen-orientation-change-when-progress-dialog-and-background-threa
Great tutorial!
I’m getting the following runtime error:
Your content must have a ListView whose id attribute is ‘android.R.id.list’
My main layout has a ListView that looks like this:
Any ideas? Thanks!
Thank you very much for your post. This is exactly what I’m looking for.
Dat
good work, thats really helpful..
Thank you very much, this was just the example I was looking for.
can u send me the project file???
This was a perfect, wonderful tutorial. Thank you so much
actually the way you handle the backing list in the adapter is a little sloppy. you don’t need to declare your own list in the adapter class, you can just use the one provided by the arrayadapter
Brilliant post, thank you.
I found the following link fixed the issue with getSystemService.
http://stackoverflow.com/questions/4321343/android-getsystemservice-inside-custom-arrayadapter
Great tutorial.
A couple of suggestions for the getView() method in the OrderAdapter class.
1. For me the line:
LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
did not work.
getSystemService() is obviously not a method of OrderAdapter. It is a method that can be provided by the Context class. In the context of an ArrayAdapter class (which is the base class for our OrderAdapter class) you can access the current context using the getContext() method. Thus, the thing that worked for me is:
LayoutInflater vi = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
2. The getView() method can be optimized. The inflating, text setting and all that processing needs to take place ONLY when the view is constructed for the first time. In other words only when the convertView parameter is null. All the other times, you just need to return the convertView as it is.
The convertView parameter acts both as an input parameter (when its initial value is null) or as an output parameter (when its initial value is something else other than null). Below is the way I re-wrote this method.
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v != null) {
LOG.d(“OrderAdapter#getview()”, “No need to do anything…”);
return v;
}
LOG.d(“OrderAdapter#getview()”, “Inflate the layout and properly set the view’s controls…”);
LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.row, null);
Order o = items.get(position);
if (o != null) {
TextView tt = (TextView) v.findViewById(R.id.toptext);
TextView bt = (TextView) v.findViewById(R.id.bottomtext);
if (tt != null) {
tt.setText(“Name: “+o.getOrderName()); }
if(bt != null){
bt.setText(“Status: “+ o.getOrderStatus());
}
}
return v;
}
This can lead to some significant optimization (especially when the list gets long enough) because the number of times when this parameter is different than null is far greater that the number of situations when it is null. I have added a couple of debug log statements to illustrate this.
This is a great post, exactly what i was looking for. I had a problem with my XML setup, but was quickly resolved. There should be more tutorials like this out there!
Awesome man!
Great example for Threading and Cool lists
Thanks!
Can you send me the project file please?
Thanks in advance.
Hi, I modified this tutorial to have a dialog that you input data. The only problem is the view is never refreshed despite putting notifyDataSetChanged(); after I add my object (you use an Order object in the tutorial). After the dialog I create a new intent and when I click back the new data isn’t there. It only comes about when the onCreate method is called. I also tried putting the thread call after I add my data but that created duplicate entries. Any ideas? Thanks
Thank you! Excellent tutorial.
@ Krzysztof: Really great tutorial. There is so much unreadable and crappy blog post regarding this out, but your’s is awesome. Thanks for it.
@ AF: I can’t second any of your comment. The original source (if not changed so far) works fine (concerning Context). And your changes concerning convertview are simply wrong: Indeed, you can use convertview, but in this case you have to update the view with the information kept in place in your items list for the given position. Otherwise you just replicate old content in your list. So the “return v” once you’ve found a convertview is wrong. You may use convertview, of course, but you have to update it with the most recent values. Same as with the iPhone table view.
Thanks a lot!! Exactly what i needed… now accessing a webservice for the list items would be awesome!
I do believe that same as Zerga, Thank you!!
Hi,
since converting my simple textual listView to something like your version above (text and image), I have lost the ability to keyboard search for elements in the listView.
Do you know if it’s possible to search a ListView when each element is actually a View in an Adapter class?
Thanks
Iain
I took the help of this tutorial. everything works fins but I am not able to use ItemClickListener. If I click on any item in list then nothing happens. I tried it with below given code:
MyAdapter adapter = new MyAdapter(this, R.layout.row, list);
lv.setAdapter(adapter);
lv.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick( AdapterView parent, View view, int position, long id) {
Log.i(“MYTAGGGGGGGGGGGGG”, “INSIDE ITEM CLICK LISTENER”);
});
android:id=”@+id/icon”
Can anyone tell me where i could get the image for the icon? Any link?
Great! Thanks a lot!
I want to do Item click on list view item.
So can i know how to do here & where to write? that is under OrderAdapter class or SoftwarePassionView ListActivity?
please reply me as soon as possible
This is the best tutorial I’ve seen for a custom listview
After hours of bashing my head against it, I realized that you can’t use the convertView if some elements of the list have different layouts and the list is large enough to require scrolling. I think the convertView passed when getView is called during a scroll is actually the one at the opposite end of the screen (say the view at the top if scrolling downward), and the result is that if those two views are of a different layout, the new one gets switched to the layout of the convertView. Anyway, I said to hell with the optimization and just inflated the view every time after spending too much time working out a more efficient solution. I hope this helps anyone else who runs into my problem.
thank you for that tutorial
It helped me a lot while I”m just beginning my journey with android!
A quick note: For the project to work you need to change “@+id/android:list” to just “@id/android:list” and “@+id/android:empty” to “@id/android:empty” in main.xml layout. Otherwise it seems to create new IDs and ListActivity then throws an error being unable to find its hard-coded value for android.R.id.list
great work dude..
cheers..
Works great. Thanks a lot!
thank you. Excellent tutorial.
I have made some modifcations for my app like “Load More Button” and kind of stuff but your tutorial showed me the path ^^
please, the method getSystemService OrderAdapter class which is defined?
Great Post, thanks! Question: why does the text view with the string “No orders to display” get displayed? It works.. but I am not sure how (noob)?. Is it because the text view is the last view in the xml file?
Thanks
Hi all,
is there a way to make the listview multiselect? I’m an absolute beginner to android and have not much idea on how can I do it.
This doesn’t work:
this.m_adapter = new OrderAdapter(this,android.R.layout.simple_list_item_multiple_choice, m_orders);
listView.setAdapter(this.m_adapter);
listView.setItemsCanFocus(false);
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
Thanks in advance!
Огромное спасибо, за доступный для понимания пример.
This is the only custom ListView example I have been able to find on the net (and I am pretty good at finding things with Google).
Thank you..
Cris..
it is amazing one; but I try :
lv1.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView a, View v, int position, long id) {
AlertDialog.Builder adb=new AlertDialog.Builder(ListViewExampleActivity.this);
adb.setTitle(“The Selected Item”);
//lv1.getItemAtPosition(position)
adb.setMessage(“Selected Item is = “+ lv1.getItemAtPosition(position).toString());
adb.setPositiveButton(“Ok”, null);
adb.show();
}
});
I want only to display the toptext of the selected item.. any idea/ ???
regards..
thank you for your post. and I have problem in:
Thread.sleep(2000);
what do it? i only know it make myprogram sleep in 2 second. but i do not understand, when I set: //Thread.sleep(2000); then I do not give any Order in my ListView.
any one answer for me? thank you so much,
Thank you so much for this code. It works perfectly. With just a few modifications, I’ve already got it to work with spinners in a listview, and now I’ve already implemented it in my app.
Given credits to you too!
There is a bug in the code, a new thread is created each time the view changes, so if go from portrait to landscape while the items are loading and the thread is sleeping, it will crash…
Thanks for this tutorial. Can you give me a clue about how to set up an itemclicklistener?
Thanks, It’s great. when an item is focused it change background to orange (very beauty). But when I clicked in an item how can I make the same focus. any body help me. Thanks.
excellent tutorial.
How would you extend the same concept to data from a database using a cursor and string of multiple values such as :
Cursor studCursor = mDbHelper.fetchAllExercisesStud(mRowId);
startManagingCursor(studCursor);
// Create an array to specify the fields we want to display in the list
String[] from = new String[]{
gradeBookDbAdapter.KEY_DIDEXERCISE,
gradeBookDbAdapter.KEY_EXSCORE,
gradeBookDbAdapter.KEY_ATTITUDE,
gradeBookDbAdapter.KEY_DTIME,
gradeBookDbAdapter.KEY_COMMENT};
I’m having an issue with the code. I’m trying to run SoftwarePassionView inside of a tabbed layout. For some reason it is blowing up to full screen, covering up the tabs as well as the title bar. Does this have to do with the vi.inflate function?
Otherwise, great code! Thanks!
Thanks..this is really helpful. But one question,
why do you need to execute the below code in the run method of the returnRes???
if(m_orders != null && m_orders.size() > 0){
m_adapter.notifyDataSetChanged();
for(int i=0;i<m_orders.size();i++)
m_adapter.add(m_orders.get(i));
}
The OrderAdapter was already passed the m_orders arraylist in the onCreate() method (OrderAdapter constructor). Shouldn't you just be able to call notifyDataSetChanged()???
Like Deepak, I wanted to know if there is any way to still use setOnItemClickListener or setTextFilterEnabled? The formatting worked perfectly but I really need to be able to click on the TextView items.
Thank you for such a nice tutorial with list view.
I followed your tutor and i was able to slove my problem to an extend.
Since you have an icon in the list view, i decided to have a checkbox and thats fine for me.
I wish to know how to accomplish questions below:
1. Firstly how to hide check box initially when the list is fetched.
2. Secondly, i have a menu, which has Edit, when i press the edit, i wish to show checkbox in the ListView.
How can i perform as such in question 1 and 2.
Please put some insight in it.
Thanks a lot for such self sufficient tutorial ….
It helped me a lot to know how to create proper Listview
after listening the Google conference video on Listview.
Great tutorial. Learning android and used this to help with my first app.
@AF – that doesn’t work as you need to update convertView based on the position index
thanks a lot.
Hi,
To access the selected item in the list, you can straightaway override ‘onListItemClick’ method since its base is a ‘ListActivity’. Use the following code and change ‘Booking’ to ‘Order’.
Thanks
@Override
protected void onListItemClick(ListView l, View v, int position, long id)
{
try
{
super.onListItemClick(l, v, position, id);
Booking bkg = (Booking)l.getItemAtPosition(position);
String strTextToDisplay = “Selected item is : ” +bkg.getBookingName();
Toast.makeText(this, strTextToDisplay, Toast.LENGTH_LONG).show();
}
catch(Exception ex)
{
Toast.makeText(this, ex.getMessage(), Toast.LENGTH_LONG).show();
}
}