Skip to content

Instantly share code, notes, and snippets.

@Psest328
Last active April 14, 2023 15:15
Show Gist options
  • Save Psest328/8762232 to your computer and use it in GitHub Desktop.
Save Psest328/8762232 to your computer and use it in GitHub Desktop.
ExpandableListView adapter with Checkboxes - BaseExpandableListAdapter that accurately tracks child checkbox states (no random checks/unchecks). This is setup for checkboxes but can be used for anything that uses true/false values
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import android.annotation.SuppressLint;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.TextView;
// Eclipse wanted me to use a sparse array instead of my hashmaps, I just suppressed that suggestion
@SuppressLint("UseSparseArrays")
public class ExpListViewAdapterWithCheckbox extends BaseExpandableListAdapter {
// Define activity context
private Context mContext;
/*
* Here we have a Hashmap containing a String key
* (can be Integer or other type but I was testing
* with contacts so I used contact name as the key)
*/
private HashMap<String, List<ExpListChildItems>> mListDataChild;
// ArrayList that is what each key in the above
// hashmap points to
private ArrayList<ExpListGroupItems> mListDataGroup;
// Hashmap for keeping track of our checkbox check states
private HashMap<Integer, boolean[]> mChildCheckStates;
// Our getChildView & getGroupView use the viewholder patter
// Here are the viewholders defined, the inner classes are
// at the bottom
private ChildViewHolder childViewHolder;
private GroupViewHolder groupViewHolder;
/*
* For the purpose of this document, I'm only using a single
* textview in the group (parent) and child, but you're limited only
* by your XML view for each group item :)
*/
private String groupText;
private String childText
/* Here's the constructor we'll use to pass in our calling
* activity's context, group items, and child items
*/
public ExpListViewAdapterWithCheckbox(Context context,
ArrayList<ExpListGroupItems> listDataGroup, HashMap<String, List<ExpListChildItems>> listDataChild) {
mContext = context;
mListDataGroup = listDataGroup;
mListDataChild = listDataChild;
// Initialize our hashmap containing our check states here
mChildCheckStates = new HashMap<Integer, boolean[]>();
}
@Override
public int getGroupCount() {
return mListDataGroup.size();
}
/*
* This defaults to "public object getGroup" if you auto import the methods
* I've always make a point to change it from "object" to whatever item
* I passed through the constructor
*/
@Override
public ExpListGroupItems getGroup(int groupPosition) {
return mListDataGroup.get(groupPosition);
}
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
@Override
public View getGroupView(int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
// I passed a text string into an activity holding a getter/setter
// which I passed in through "ExpListGroupItems".
// Here is where I call the getter to get that text
groupText = getGroup(groupPosition).getMyText();
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) mContext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.group_item, null);
// Initialize the GroupViewHolder defined at the bottom of this document
groupViewHolder = new GroupViewHolder();
groupViewHolder.mGroupText = (TextView) convertView.findViewById(R.id.groupTextView);
convertView.setTag(groupViewHolder);
} else {
groupViewHolder = (GroupViewHolder) convertView.getTag();
}
groupViewHolder.mGroupText.setText(groupText);
return convertView;
}
@Override
public int getChildrenCount(int groupPosition) {
return mListDataChild.get(mListDataGroup.get(groupPosition).getMyText()).size();
}
/*
* This defaults to "public object getChild" if you auto import the methods
* I've always make a point to change it from "object" to whatever item
* I passed through the constructor
*/
@Override
public ExpListChildItems getChild(int groupPosition, int childPosition) {
return mListDataChild.get(mListDataGroup.get(groupPosition).getMyText()).get(childPosition);
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
final int mGroupPosition = groupPosition;
final int mChildPosition = childPosition;
// I passed a text string into an activity holding a getter/setter
// which I passed in through "ExpListChildItems".
// Here is where I call the getter to get that text
childText = getChild(mGroupPosition, mChildPosition).getChildText();
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) this.mContext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.child_item, null);
childViewHolder = new ChildViewHolder();
childViewHolder.mChildText = (TextView) convertView
.findViewById(R.id.childTextView);
childViewHolder.mCheckBox = (CheckBox) convertView
.findViewById(R.id.checkBox);
convertView.setTag(R.layout.child_item, childViewHolder);
} else {
childViewHolder = (ChildViewHolder) convertView
.getTag(R.layout.child_item);
}
childViewHolder.mChildText.setText(childText);
/*
* You have to set the onCheckChangedListener to null
* before restoring check states because each call to
* "setChecked" is accompanied by a call to the
* onCheckChangedListener
*/
childViewHolder.mCheckBox.setOnCheckedChangeListener(null);
if (mChildCheckStates.containsKey(mGroupPosition)) {
/*
* if the hashmap mChildCheckStates<Integer, Boolean[]> contains
* the value of the parent view (group) of this child (aka, the key),
* then retrive the boolean array getChecked[]
*/
boolean getChecked[] = mChildCheckStates.get(mGroupPosition);
// set the check state of this position's checkbox based on the
// boolean value of getChecked[position]
childViewHolder.mCheckBox.setChecked(getChecked[mChildPosition]);
} else {
/*
* if the hashmap mChildCheckStates<Integer, Boolean[]> does not
* contain the value of the parent view (group) of this child (aka, the key),
* (aka, the key), then initialize getChecked[] as a new boolean array
* and set it's size to the total number of children associated with
* the parent group
*/
boolean getChecked[] = new boolean[getChildrenCount(mGroupPosition)];
// add getChecked[] to the mChildCheckStates hashmap using mGroupPosition as the key
mChildCheckStates.put(mGroupPosition, getChecked);
// set the check state of this position's checkbox based on the
// boolean value of getChecked[position]
childViewHolder.mCheckBox.setChecked(false);
}
childViewHolder.mCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
boolean getChecked[] = mChildCheckStates.get(mGroupPosition);
getChecked[mChildPosition] = isChecked;
mChildCheckStates.put(mGroupPosition, getChecked);
} else {
boolean getChecked[] = mChildCheckStates.get(mGroupPosition);
getChecked[mChildPosition] = isChecked;
mChildCheckStates.put(mGroupPosition, getChecked);
}
}
});
return convertView;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return false;
}
@Override
public boolean hasStableIds() {
return false;
}
public final class GroupViewHolder {
TextView mGroupText;
}
public final class ChildViewHolder {
TextView mChildText;
CheckBox mCheckBox;
}
}
@mishozhghenti
Copy link

Nice.

@WillMattt
Copy link

WillMattt commented Jun 8, 2016

Many thanks Psest328! I have implemented your code and can see the expandablelistview with checkboxes. How can I check which checkboxes are selected from the MainActivity? I am stuck at this point. Any tipps are greatly appreciated.
P.S.: Do you know of an easy way to adapt your approach in case there is more than one child in a group?

@aliceleaf
Copy link

Thanks! I have my own implementation of keeping track of the checked state, but the key factor you helped me with is setting the OnCheckedChangeListener to null before restoring the checkbox state, and then setting the OnCheckedChangedListener for the checkbox.

@jyothishkm
Copy link

thanks! but how to set group checkbox position and when i select group checkbox all the child checkbox select.

Copy link

ghost commented Feb 18, 2017

Great solution, thanks!

@ashokbg
Copy link

ashokbg commented Apr 6, 2017

on-expand , collapse group then checked checkbox getting unchecked...

@amritesh14
Copy link

Great solution , it is very useful. Thanks!!!

@amritesh14
Copy link

Hi ,Is there any way to store "HashMap<Integer, boolean[]> mChildCheckStates=new HashMap<Integer, boolean[]>();" into shared preferences or any other storage.

@shubhrajps
Copy link

Thanks a lot for the example. I just want to know how am I supposed the retrieve the items selected.

@jay2011patel
Copy link

thanks man you do useful work

Copy link

ghost commented Mar 9, 2018

Hey is there a special reason why the same code will be executed on if and else? ^^

`
childViewHolder.mCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {

		@Override
		public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
			
			if (isChecked) {

				boolean getChecked[] = mChildCheckStates.get(mGroupPosition);
				getChecked[mChildPosition] = isChecked;
				mChildCheckStates.put(mGroupPosition, getChecked);
							
			} else {

				boolean getChecked[] = mChildCheckStates.get(mGroupPosition);
				getChecked[mChildPosition] = isChecked;
				mChildCheckStates.put(mGroupPosition, getChecked);
			}
		}
	});

`

@chnouman
Copy link

working example would be to helpful if anybody have.

@NomiRizwan
Copy link

HI i am new in development, can anybody share the xml to use. thanks in advance to all.

@20244
Copy link

20244 commented Jun 18, 2018

Hi, I want to getText of selected items, Can anyone please help with this?

@samuellim18
Copy link

image

Hi, I seem to get this recurring problem when I copied and pasted the code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment