Pupulating and Interacting with Listview Control in Android

Standard

Hmmm… What is a ListView???

It’s very commonplace to see ListView controls in android apps. They are a very useful tool when we need to show data to our app’s users (See the image bellow).

ListVIew sample

I have been currently working with several ListView controls and it has become really easy to manage them when you finally understand the way they are implemented.

A ListView control is able to handle as many columns and as many rows as we need. A row may refer to an object inside our ListView. In the other hand, a column may refer to an attribute of the object. It’s quite simple to design and manage our ListView no matter how many columns or rows we need.

Let’s Work with an Example!

For this post, i’m going to take a common application as example. Imagine we need an application which uniquely has one ListView control. The app’s requirements are listed next:

  • List view must have 4 columns. Id (TextView), name(TextView), phone(TextView), delete(Button).
  • Rows must be added dynamically.
  • ListView must have a header which indicated the columns names.
  • Rows must alternate between two different background colors.
  • After an item has been added, it can be deleted when we click the “delete” control.

In order to complete such requirements, lets begin with our App!

Starting to Code.

First, let’s create an empty activity with a vertical layout as root element. Next, add a new ListView control and set its attributes layout:width and layout:height to match_parent. Notice that we will need the whole screen for this app, and it automatically implements an “scrolling” behavior that we don’t need to care about when adding more and more rows.

But, where is the header? How will we visualize such strings?. It is simple, just ad an HorizontalLayout above our ListView and fill it with the 4 column names we need (Notice the last one will have a blank text).

As we have our ListView and the new HorizontalLayout at the same level, we need to establish the property layout:weight. After this we will have something like this:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight=".05">

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:text="ID"
            android:id="@+id/head1"
            android:layout_weight="1"
            android:gravity="center_vertical|center_horizontal" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:text="Name"
            android:id="@+id/head2"
            android:layout_weight="1"
            android:gravity="center_vertical|center_horizontal" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:text="Phone"
            android:id="@+id/head3"
            android:layout_weight="1"
            android:gravity="center_vertical|center_horizontal" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:id="@+id/head4"
            android:layout_weight="1" />
    </LinearLayout>

    <ListView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:id="@+id/listView"
        android:layout_weight=".95" />
</LinearLayout>

The next step is to develop our rows design. We need to customize every row we will add later. Every row is based over an schema. This schema has three TextView controls (to show id, name and phone values) and a Button (to delete the row from the ListView).

Create a new layout file and name it list_row.xml. This item will have the next content:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:text="row1"
        android:id="@+id/column1"
        android:layout_weight="1"
        android:layout_gravity="center_vertical" />

    <TextView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:text="row2"
        android:id="@+id/column2"
        android:layout_weight="1"
        android:layout_gravity="center_vertical" />

    <TextView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:text="row3"
        android:id="@+id/column3"
        android:layout_weight="1"
        android:layout_gravity="center_vertical" />

    <Button
        android:layout_width="32dp"
        android:layout_height="32dp"
        android:id="@+id/column4"
        android:background="@drawable/ic_delete"
        android:layout_marginTop="5dp"
        android:layout_marginBottom="5dp"
        android:layout_gravity="center_vertical" />

</LinearLayout>

Notice the image over the Button’s background. This image can be found here: ic_delete.png. Download the 32×32 icon in red color and add it to the drawable folder.

The next step is to create a new Class in order to store all the objects in a List and later set it as our ListView adapter.

So, create a new class and name it User. The content of this class must look as follows:

public class User {
    private String id,name,phone;
    public String getPhone() {
        return phone;
    }
    public void setPhone(String phone) {
        this.phone = phone;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public User(String id, String name, String phone) {
        this.id = id;
        this.name = name;
        this.phone = phone;
    }
}

Notice the constructor which receives all three values to the object we need to instance later. Getter and Setters will be also useful in the later code.

Next, as we are implementing a custom style for our ListView, we need to customize the adapter as well. This is needed to make the adapter capable to add our custom rows to our ListView control.

Create a new class and name it UserAdapter with the following content:

public class UserAdapter  extends BaseAdapter {
    private Activity activity;
    private List<User> usersList;

    public UserAdapter(Activity activity, List<User> usersList) {
        super();
        this.activity = activity;
        this.usersList = usersList;
    }
    @Override
    public int getCount() {
        return usersList.size();
    }
    @Override
    public Object getItem(int position) {
        return usersList.get(position);
    }
    @Override
    public long getItemId(int position) {
        // TODO Auto-generated method stub
        return 0;
    }
    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            LayoutInflater inflater = activity.getLayoutInflater();
            convertView = inflater.inflate(R.layout.list_row, null);
        }
        TextView col1 = (TextView) convertView.findViewById(R.id.column1);
        TextView col2 = (TextView) convertView.findViewById(R.id.column2);
        TextView col3 = (TextView) convertView.findViewById(R.id.column3);
        Button col4 = (Button) convertView.findViewById(R.id.column4);

        col1.setText(usersList.get(position).getId());
        col2.setText(usersList.get(position).getName());
        col3.setText(usersList.get(position).getPhone());
        col4.setTag(position);
        col4.setOnClickListener(
                new Button.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        usersList.remove(position);
                        notifyDataSetChanged();
                    }
                }
        );
        /*Alternate color between rows*/
        if (position % 2 == 1)
            convertView.setBackgroundColor(Color.WHITE);
        else
            convertView.setBackgroundColor(Color.CYAN);
        return convertView;
    }
}

We need to pay attention specially for this class. The first thing we have to note is that it extends from BaseAdapter, which allow us to override its main methods. We base all the functionality over a List we receive as parameter, containing User’s objects (Here is where our getters make sense). 

The most important method here is the getView. Here we receive the calling to create a new row, inflating our custom row from the xml file and setting its respective values from the item found over the List in position parameter. The next relevant thing is the Button’s listener inside our adapter. Since this Button needs to delete the row where it belong to, all we need to do is to delete the object from List in the received position and notify our adapter that dataset has changed. This will automatically tell the ListView to refresh its data later on. Finally, in order to alternate background color we just set the property BackgroundColor to an specific color to even rows and another color to odd rows (we alternate white and cyan for this example).

Populating our ListView

At this point, all is ready to begin populating our ListView dynamically. Back in our main activity, we need a List of type User and an adapter of type UserAdapter (yes, our custom adapter).  Our main activity Class must look something like this:

public class MainActivity extends AppCompatActivity {
    /*Declaring our objects*/
    private List<User> usersList;
    private UserAdapter adapter;

    /*Our ListView*/
    private ListView listView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        usersList = new ArrayList<User>();
        adapter = new UserAdapter(this, usersList);
        listView = (ListView)findViewById(R.id.listView);
    }
}

And we are good to start adding items to our ListView. For instance, we will add rows inside a loop to make it easy. If you need to adjust this fill functionality to another way it will result you very easy, item by item, fill it from a Json Object, from a database query, etc.

Finally just create a new function which populates our ListView from a loop. It must look like this:

private void PopulateListView()
{
    /*Add 20 rows*/
    for(int i = 0; i < 20; i++)
    {
        usersList.add(new User(String.valueOf(i + 1), "User" + String.valueOf(i), "Phone" + String.valueOf(i)));
    }
    /*Load the List content in our ListView by setting the adapter*/
    listView.setAdapter(adapter);
}

And just call it inside the onCreate method or from wherever you need it. Thats it, when you run the app you must be able to see something like the following image:

ListViewSampel

Our final result! 🙂

Whenever you click the delete button, the item will be removed both from the List and from ListView.

As you see, implementing ListView controls this way result quite simple and flexible to scaling when its required. That’s it for now, see you until the next post!

Leave a Reply

Your email address will not be published. Required fields are marked *