Today I am going to show how to deal with Custom ListView having Chekbox. Many developers are facing the issue of Checkbox item getting uncheck or check while scrolling the ListView. So, I will make it clear to developers how to deal with ListView having Checkbox. You can learn about the recycling of view in ListView from this Blog.
The issue with CheckBox inside ListView is that the view gets recycled due to recycling of ListView and the value of Checkbox(check or uncheck) is not maintained. To, maintain the state to CheckBox there has to be something that can store the state of Checkbox.
So, we have a Model class that will have name and selected property of ListView row having TextView and CheckBox.
Model.java
public class Model {
private String name;
private boolean selected;
public Model(String name) {
this.name = name;
}
public String getName() {
return name;
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
}
The issue with CheckBox inside ListView is that the view gets recycled due to recycling of ListView and the value of Checkbox(check or uncheck) is not maintained. To, maintain the state to CheckBox there has to be something that can store the state of Checkbox.
So, we have a Model class that will have name and selected property of ListView row having TextView and CheckBox.
Model.java
public class Model {
private String name;
private boolean selected;
public Model(String name) {
this.name = name;
}
public String getName() {
return name;
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
}
Now, we will setup the main Activity that will set the Adapter for the Custom ListView.
MainActivity.java
public class MainActivity extends Activity implements OnItemClickListener{
ListView listView;
ArrayAdapter<Model> adapter;
List<Model> list = new ArrayList<Model>();
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
listView = (ListView) findViewById(R.id.my_list);
adapter = new MyAdapter(this,getModel());
listView.setAdapter(adapter);
listView.setOnItemClickListener(this);
}
@Override
public void onItemClick(AdapterView<?> arg0, View v, int position, long arg3) {
TextView label = (TextView) v.getTag(R.id.label);
CheckBox checkbox = (CheckBox) v.getTag(R.id.check);
Toast.makeText(v.getContext(), label.getText().toString()+" "+isCheckedOrNot(checkbox), Toast.LENGTH_LONG).show();
}
private String isCheckedOrNot(CheckBox checkbox) {
if(checkbox.isChecked())
return "is checked";
else
return "is not checked";
}
private List<Model> getModel() {
list.add(new Model("Linux"));
list.add(new Model("Windows7"));
list.add(new Model("Suse"));
list.add(new Model("Eclipse"));
list.add(new Model("Ubuntu"));
list.add(new Model("Solaris"));
list.add(new Model("Android"));
list.add(new Model("iPhone"));
list.add(new Model("Java"));
list.add(new Model(".Net"));
list.add(new Model("PHP"));
return list;
}
}
ListView listView;
ArrayAdapter<Model> adapter;
List<Model> list = new ArrayList<Model>();
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
listView = (ListView) findViewById(R.id.my_list);
adapter = new MyAdapter(this,getModel());
listView.setAdapter(adapter);
listView.setOnItemClickListener(this);
}
@Override
public void onItemClick(AdapterView<?> arg0, View v, int position, long arg3) {
TextView label = (TextView) v.getTag(R.id.label);
CheckBox checkbox = (CheckBox) v.getTag(R.id.check);
Toast.makeText(v.getContext(), label.getText().toString()+" "+isCheckedOrNot(checkbox), Toast.LENGTH_LONG).show();
}
private String isCheckedOrNot(CheckBox checkbox) {
if(checkbox.isChecked())
return "is checked";
else
return "is not checked";
}
private List<Model> getModel() {
list.add(new Model("Linux"));
list.add(new Model("Windows7"));
list.add(new Model("Suse"));
list.add(new Model("Eclipse"));
list.add(new Model("Ubuntu"));
list.add(new Model("Solaris"));
list.add(new Model("Android"));
list.add(new Model("iPhone"));
list.add(new Model("Java"));
list.add(new Model(".Net"));
list.add(new Model("PHP"));
return list;
}
}
main.xml
<?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="fill_parent"
android:orientation="vertical" >
<ListView
android:id="@+id/my_list"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ListView
android:id="@+id/my_list"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
row.xml
<?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="wrap_content" >
<TextView
android:id="@+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@+id/label"
android:textSize="30sp" >
</TextView>
<CheckBox
android:id="@+id/check"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginLeft="4dip"
android:layout_marginRight="10dip"
android:focusable="false"
android:focusableInTouchMode="false" >
</CheckBox>
</RelativeLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@+id/label"
android:textSize="30sp" >
</TextView>
<CheckBox
android:id="@+id/check"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginLeft="4dip"
android:layout_marginRight="10dip"
android:focusable="false"
android:focusableInTouchMode="false" >
</CheckBox>
</RelativeLayout>
Finally, now we will have the Adapter class.
MyAdapter.java
public class MyAdapter extends ArrayAdapter<Model> {
private final List<Model> list;
private final Activity context;
boolean checkAll_flag = false;
boolean checkItem_flag = false;
public MyAdapter(Activity context, List<Model> list) {
super(context, R.layout.row, list);
this.context = context;
this.list = list;
}
static class ViewHolder {
protected TextView text;
protected CheckBox checkbox;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
if (convertView == null) {
LayoutInflater inflator = context.getLayoutInflater();
convertView = inflator.inflate(R.layout.row, null);
viewHolder = new ViewHolder();
viewHolder.text = (TextView) convertView.findViewById(R.id.label);
viewHolder.checkbox = (CheckBox) convertView.findViewById(R.id.check);
viewHolder.checkbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
int getPosition = (Integer) buttonView.getTag(); // Here we get the position that we have set for the checkbox using setTag.
list.get(getPosition).setSelected(buttonView.isChecked()); // Set the value of checkbox to maintain its state.
}
});
convertView.setTag(viewHolder);
convertView.setTag(R.id.label, viewHolder.text);
convertView.setTag(R.id.check, viewHolder.checkbox);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.checkbox.setTag(position); // This line is important.
viewHolder.text.setText(list.get(position).getName());
viewHolder.checkbox.setChecked(list.get(position).isSelected());
return convertView;
}
}
private final List<Model> list;
private final Activity context;
boolean checkAll_flag = false;
boolean checkItem_flag = false;
public MyAdapter(Activity context, List<Model> list) {
super(context, R.layout.row, list);
this.context = context;
this.list = list;
}
static class ViewHolder {
protected TextView text;
protected CheckBox checkbox;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
if (convertView == null) {
LayoutInflater inflator = context.getLayoutInflater();
convertView = inflator.inflate(R.layout.row, null);
viewHolder = new ViewHolder();
viewHolder.text = (TextView) convertView.findViewById(R.id.label);
viewHolder.checkbox = (CheckBox) convertView.findViewById(R.id.check);
viewHolder.checkbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
int getPosition = (Integer) buttonView.getTag(); // Here we get the position that we have set for the checkbox using setTag.
list.get(getPosition).setSelected(buttonView.isChecked()); // Set the value of checkbox to maintain its state.
}
});
convertView.setTag(viewHolder);
convertView.setTag(R.id.label, viewHolder.text);
convertView.setTag(R.id.check, viewHolder.checkbox);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.checkbox.setTag(position); // This line is important.
viewHolder.text.setText(list.get(position).getName());
viewHolder.checkbox.setChecked(list.get(position).isSelected());
return convertView;
}
}
So, the important line in the above Adapter class is to setTag() position of CheckBox and then retrieve it using getTag() inside onCheckedChanged() and then set the current state of CheckBox in your Model class instance. I am also attaching the complete source code for the same so that anyone can download and understand that how it works. Here is the complete source code.
Hello TUM BIN,
ReplyDeleteThanks for sharing such helping code, This is very common issue faced by the android newbie like me, Your blog entry made me clear, Now I know what to do for customizing my ListView row with CheckBox, thanks again and finger crossed for next post. :)
Cheers!
this is a great job you have again started to share. Please keep this process continue.
ReplyDeleteAnd i know you have written this article based on the StackOverflow community member's problem. So this is really a fantastic job to resolve many persons issue.
Thanks alot buddy i really needed it. Really a helpful post for beginners. Thanks again :)
ReplyDeleteGood post, however I've encountered a bug.
ReplyDeleteAs the view is being stored, the view does not reflect changes in the data set. I found that despite notifyDataSetChanged() being called, the viewholder can still hold old data.
So before
if (convertView == null) {
I had to check the viewholder against my data set, if I don't have the data in the set then set convertview to null.
This can start getting very inefficient if the data set is large.
That can happen if you are not setting the new data inside the POJO class.
Deleteawesomeeeeeeeeee I've been luking fr a perfect example bt i found it here.....
ReplyDeletethnx a 1000 times!
Monika!
I want a lil help also in it...in model class suppose i want 1 more field...like in the listciew instead of jst 1 field i want two textviews then???? thnx in advance...m sure u wud b having a sure shot solution...!
DeleteThanks for sharing this good and nice code
ReplyDelete@Monika you just need to add a View in your row.xml and also variable with getter-setter in Model class and perform accordingly as done for Checkbox and TextView.
ReplyDeleteThanks!
ReplyDeleteThank you, it saved my life)
ReplyDeleteThank's for this great tutoriel ! But how can I get the checked items in order to send them to an other intent !!
ReplyDeletein you getView do following:
ReplyDeleteviewHolder.checkbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
int getPosition = (Integer) buttonView.getTag(); // setTag.
}
});
convertView.setTag(viewHolder);
convertView.setOnClickListener(new MyCustomItemClickListener(
));
return convertView;
}// getView ends
class MyCustomItemClickListener implements OnClickListener {
MyCustomItemClickListener() {
/*Whatever initialization you need can be done here. You can pass values in constructor when calling it from getview and use with intent extras
*/
}
@Override
public void onClick(View arg0) {
// Launch Activity
Intent showImg = new Intent(mContext, YourActivity.class);
mContext.startActivity
}
}
}
Great to come accross something that actually works, and at the same time is not hopelessly complicated. It's also very helpful that you universalized the code. Thanks,
ReplyDeleteDirk
thx for " viewHolder.checkbox.setChecked(list.get(position).isSelected());
ReplyDelete" tip :)
Thnx...
ReplyDeleteI really like i am trying from last 2 days but bcoz of u it solve..
Thnx again
Thanks a lot :)
ReplyDeleteThank you very much for the help. I'm still in shock that this is the simplest way to distinguish clicks on a check box or an item.
ReplyDeletenow How to get all checked item. any one help me ? plzzzzzz
ReplyDeleteNice tutorial.. Thanks a lot.. Keep on post tutorials like this :)
ReplyDeleteexcellent !
ReplyDeleteThanks Friend!
ReplyDeleteI have seek solution for the same what you have posted friend..,
Thank you very much..,