<

Using A Custom Array Adapter

Published on
6,106 Points
1,506 Views
1 Endorsement
Last Modified:
Chris Harte
A developer with over twenty years experience. Started on mainframes and Cobol, now on mobiles and Android.
Displaying an arrayList in a listView using the default adapter is rarely the best solution. To get full control of your display data, and to be able to refresh it after editing, requires the use of a custom adapter.
I have a database with lots of stored information. I extract it into an array and display it using a listView. All nice and simple, except I want to limit the amount of data I am displaying. The information is editable and the listView does not update the display with the new, edited, item. To overcome these limitations I am going to use a custom adapter.

I have some data loaded from a local database; each item is a data object called Sticky_notes. These are collected into an arrayList and I want to display them in a listView. The list and default adapter looks like this:

 
    private ArrayList<Sticky_notes>    notes;
    private ArrayAdapter<Sticky_notes> my_adapter;

Open in new window


The adapter is then initialised and used:
 
    my_adapter = new ArrayAdapter<Sticky_notes>(this,
                                  android.R.layout.simple_list_item_1,
                                  notes);

    list_view.setAdapter(my_adapter);

Open in new window

This will display all the fields in my database. But I wanted to show only fields that are relevant. This is what my data class looks like
 
public class Sticky_notes
{
    private String row_id;
    private String title;
    private String note;

    public Sticky_notes (String row_id, String title, String note)
    {
        super();
        this.row_id = row_id;
        this.title = title;
        this.note = note;
    }

    public String get_title()
    {
        return title;
    }
    
    
    public String get_note()
    {
        return note;
    }    

}    

Open in new window


Each item was a Sticky_notes stored in an SQLite database. I have extracted all the data into the ArrayList consisting of Sticky_notes. I only want to display 'title' and 'note'; the row_id is used internally for editing so nobody needs to see it. When you click on an item you can edit it. The displayed list view then has to be refreshed. This is handled through the adapter. 

Create an array adapter as a class that extends the arrayAdapter. This will have its constructor and one implemented method of getView(); 
 
private static class Note_Adapter extends ArrayAdapter<Sticky_notes>
{
    public Note_Adapter(Activity context, ArrayList<Sticky_notes> data)
     {
         // The layout containing the listView where the data is going to end up.
         super(context, R.layout.activity_sticky, data);
     }

     @Override public View getView(int position, View convertView, ViewGroup parent)
    {
    }

}

Open in new window

The listView typically has far more data than the number of displayed rows. When the list is scrolled then the rows and their associated data are out of the visible area. The objects for the rows can be reused for the newly visible rows. This is what the getView method is for. This method will use a local static class that will hold the view data, I have called this class Note_holder.
 
private static class Note_Adapter extends ArrayAdapter<Sticky_notes>
{
     // The class for holding the view data
     private static class Note_Holder
     {
         TextView txtNote;
         TextView txtTitle;
     }

    public Note_Adapter(Activity context, ArrayList<Sticky_notes> data)
     {
         // The layout containing the listView where the data is going to end up.
         super(context, R.layout.activity_sticky, data);
     }

     @Override public View getView(int position, View convertView, ViewGroup parent)
    {
    }

}

Open in new window


The Note_holder will hold the reference to the relevant view in your layout, which will be set using the setTag() method. When there is no current view the XML is inflated into convertView, using findViewById to populate it. If it is already being used we will get a convertView object that will update the Note_holder via the getTag() method. Sounds complex because it is, but it is much faster than using findViewById() every single time.

In the getView method instantiate the Note_holder, test the convertView object and if null then assign the new and relevant attributes. The item that was clicked is passed in the 'position' parameter. If convertView is not null, reuse it.
 
private static class Note_Adapter extends ArrayAdapter<Sticky_notes>
{

     private static class Note_Holder
     {
         TextView txtNote; // The data is made up of Note and Title
         TextView txtTitle;
     }

    public Note_Adapter(Activity context, ArrayList<Sticky_notes> data)
     {
         // The layout containing the listView where the data is going to end up.
         super(context, R.layout.activity_sticky, data);
     }

     @Override public View getView(int position, View convertView, ViewGroup parent)
    {

         Note_Holder holder;  // The viewed data

         if (convertView == null) 
            {
               // There was not existing object, so inflate the xml into it.
                holder = new Note_Holder();

                // Inflate the layout containing the textViews that I want to
                // add to the listView
                LayoutInflater inflater = LayoutInflater.from(getContext());
                convertView = inflater.inflate(R.layout.note_item, parent, false);

                holder.txtTitle = (TextView) convertView.findViewById(R.id.item_title);
                holder.txtNote = (TextView) convertView.findViewById(R.id.item_note);

                //The convertView has been used, so set the tag with the view information
                convertView.setTag(holder);

            }
            else
            {
                // We have a convertView, so get the view information from the tag.
                holder = (Note_Holder) convertView.getTag();
            }

            // Fill the Note_holder class with data that has been passed from the listView
            Sticky_notes temp_data = getItem(position);

            String title = temp_data.get_title(); //The get methods from the data class.
            String note = temp_data.get_note();

            holder.txtTitle.setText("\n  " + title);
            holder.txtNote.setText(" " + note);

            return convertView;

    }

}

Open in new window


This adapter is now ready to be used to display your data. Comment out the default adapter and use this new one.
 
    /* my_adapter = new ArrayAdapter<Sticky_notes>(this,
                                     android.R.layout.simple_list_item_1,
                                     notes); 
    */

    my_adapter = new Note_Adapter(this, notes);

    list_view.setAdapter(my_adapter);

Open in new window

When the variable 'notes' has been populated with the contents of your database the new adapter will display it to the available amount of screen. When you create your onClickListener to enable editing of your item you can use this adapter to access your data:
 
       list_view.setOnItemClickListener(new OnItemClickListener()
       {
           @Override 
           public void onItemClick(AdapterView<?> parent, View view, int position, long id)
           {
                //Use the adapter to get the item at 'position'
                String note_body = my_adapter.getItem(position).get_note();

                //This will edit the clicked item.
                Intent i = new Intent("app.simple_sticky.EDIT_NOTE");           
                i.putExtra("note", note_body);                
                startActivity(i);
            }
        });

Open in new window


What you will find now is that after you have edited your data, the display will not use the new information. So before setting the adapter, use the library methods clear() and addAll() to rebuild the array. In your onCreate() and onResume() methods you will need
 
 my_adapter.clear(); // empty the adapter
 my_adapter.addAll(notes); // Load it up with the updated arraylist.
 list_view.setAdapter(my_adapter);

Open in new window

This will display your data from the database and, more importantly, refresh the display after editing without having to reload it from the database.
1
Comment
Author:Chris Harte
0 Comments

Featured Post

Ultimate Tool Kit for Technology Solution Provider

Broken down into practical pointers and step-by-step instructions, the IT Service Excellence Tool Kit delivers expert advice for technology solution providers. Get your free copy now.

Join & Write a Comment

When you create an app prototype with Adobe XD, you can insert system screens -- sharing or Control Center, for example -- with just a few clicks. This video shows you how. You can take the full course on Experts Exchange at http://bit.ly/XDcourse.
Starting up a Project

Keep in touch with Experts Exchange

Tech news and trends delivered to your inbox every month