Android development blog
Tutorials about Android dev topics

Listview with Checkbox - Custom Adapter

by Francesco Azzola, February 7, 2013
We come back discussing some other application of ListView. This is an important component and it is widely used and has many applications in different fields. In this post, i want to show how to use ListView with Checkbox so that user can select items and give you some tricks to handle some problems.
We want to implement a ListView where each item has a checkbox like the figures shown below:

android_listview_checkbox_item
 android_listview_checkbox_item_check

At the same time we want to handle user click so that we keep track of items checked and give access to the item menu.
Let’s start.

Implement a layout

The first thing we have to do is implementing the layout like the the one shown below:
<?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"
    >

    <CheckBox android:id="@+id/chk"
              android:layout_alignParentLeft="true"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              />
    
    <TextView
        android:id="@+id/name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1" 
        android:textStyle="bold"
        android:layout_toRightOf="@id/chk"
        />

    <TextView
        android:id="@+id/dist"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:textSize="8dp"
        android:textStyle="italic"
        android:layout_below="@id/name"
        android:layout_toRightOf="@id/chk"/>

</RelativeLayout>

This layout implements the structure shown in the pictures above.

The Adapter AND USER Events


Once we have our layout, we use it in our Adapter. As discussed in other post (Android ListView : Custom Adapter And Layout and Android ListView : ArrayAdapter, Manage Items, User Interaction), we have to modify our getView method so that we can handle the checkbox. The code is very similar to others used in other post, we simply need to get the reference to the Checkbox component and handle it.
CheckBox chk = (CheckBox) v.findViewById(R.id.chk);
....
holder.chk = chk;
chk.setOnCheckedChangeListener( (MainActivity) context);

In the last line we simply define the listener invoked when user clicks on the Checkbox. Java purists can discus if it is better to implements the listener directly inside the adapter or somewhere else (MainActivity in our case). Well in my vision the adapter should be used just for filling the ListView, so that MainActivity class is my opinion should the one that handles user touch interactions.

The MainActivity becomes:
public class MainActivity extends Activity implements android.widget.CompoundButton.OnCheckedChangeListener {
.....


    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
         int pos = lv.getPositionForView(buttonView);
         System.out.println("Pos ["+pos+"]");
         if (pos != ListView.INVALID_POSITION) {
             Planet p = planetsList.get(pos);         
             p.setChecked(isChecked);
         }
        
    }
}

The onCheckedChanged method is invoked when user clicks on the checkbox. In this method we handle the event, finding first the position or in other words the item position inside the ListView. Then we verify that this position is valid, comparing it against ListView.INVALID_POSITION and if everything is ok we set the boolean attribute of the Planet class the the isChecked parameter value.

Running the app you have this result:

android_listview_with_checkbox_item

Analysis of some “WIRED” behaviours


If you “play” with the app you could notice some strange behaviours. It crashes!!!…Yes right. It happens that in some circumstances it suddenly crashes as you check the checkbox. Why?! Well this is a mystery in my opinion. If you read the log you get a NullPointerException in getPositionView. It could be because reuse the View in the ListView to optimize the resources. Anyway, investigating the problem it seems to me that the check listener is attached to an invalid checkbox that i suppose it is caused by the View re-use. The solution (but i don’t know if it is correct) is the “refresh” the listener reference everytime we get a item view reference. So in our adapter we have to modify a bit the code:
//chk.setOnCheckedChangeListener( (MainActivity) context); // We have to remove it it causes NPE

...

holder.chk.setOnCheckedChangeListener( (MainActivity) context);

Another “strange” thing, maybe you already noticed, it is the fact we can’t select the item anymore so if you long press on the item the menu doesn’t appear anymore. There’s a solution explained in this link, this behaviour is caused by components that “locks” the focus. If you read the solution you can find that it is enough blocking focus on descendants, like that:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:descendantFocusability="blocksDescendants">

..

</RelativeLayout>

Using this trick if we run the app we have:

android_listview_with_checkbox_issue

3 comments:

  1. Nice post.Give it up. Thanks for share this article. For more visit:Web App Development

    ReplyDelete
  2. Nice tutorial with some errors though. Thing you see as weird behaviour is actually an error in your code. The error is in onCheckedChanged function. You are acting as checkbox widget has it's own position in listView and that's not the fact. It is contained in relativeLayout that has position in listView and when you try to find position of checkbox, application hangs because you are trying to find something that doesn't exist.

    Solution is to create tag on checkbox with the position in getView function in adapter:

    holder.checkbox.setTag(position);

    and then you can get position of item in listView in the following way:

    int pos = (Integer)buttonView.getTag();

    instead of

    int pos = lv.getPositionForView(buttonView);

    Anyway, nice tutorial :)

    ReplyDelete
  3. Hello Admin,

    Can you please provide full source code of example. I had try to make it with your referd link but i face error.

    ReplyDelete

Related Posts with Thumbnails