CheckBox states in ExpandableListView's children
Asked Answered
G

5

5

I've read about 30 pages from SO as well as tutorials about tracking check states in lists but info (especially working info) is scarce on doing so in an Expandable ListView.

I've got the children populating with checkboxes but when I check a box on a child from 1 group, random children in other groups also check. I need to stop this. The best info I think I read was to set the checkbox as a separate tag but I don't know how to set multiple tags, when I tried, I got a class mismatch error comparing the checkbox to the convertview.

Has anybody come across a good way to keep track of checkbox states in an expandablelistview's children?

I've made multiple changes so I'm putting in the entire adapter code. Please check first few lines of getGroupView and entire getChildView and help me see what I'm doing wrong.

EDIT: What's happening now is that when I check a box and then expand another group, all checked boxes uncheck:

public class MyExpandableListAdapter extends BaseExpandableListAdapter
    implements OnCheckedChangeListener {

private Context mContext;

private ArrayList<ContactNameItems> mListDataHeader;
private HashMap<String, List<ContactPhoneItems>> mListDataChild;

private boolean[] mGetChecked;
private HashMap<String, boolean[]> mChildCheckStates;

private ArrayList<String> selectedNumbers;

private ChildViewHolder childViewHolder;
private GroupViewHolder groupViewHolder;

private String numberText;

public MyExpandableListAdapter(Context context,
        ArrayList<ContactNameItems> listDataHeader,
        HashMap<String, List<ContactPhoneItems>> listDataChild,
        ArrayList<String> listOfNumbers) {

    mContext = context;
    mListDataHeader = listDataHeader;
    mListDataChild = listDataChild;
    selectedNumbers = listOfNumbers;

    mChildCheckStates = new HashMap<String, boolean[]>();
}

@Override
public int getGroupCount() {
    return mListDataHeader.size();
}

@Override
public ContactNameItems getGroup(int groupPosition) {
    return mListDataHeader.get(groupPosition);
}

@Override
public long getGroupId(int groupPosition) {
    return groupPosition;
}

@Override
public View getGroupView(int groupPosition, boolean isExpanded,
        View convertView, ViewGroup parent) {

    String contactName = getGroup(groupPosition).getName();
    Bitmap contactImage = getGroup(groupPosition).getImage();

    mGetChecked = new boolean[getChildrenCount(groupPosition)];
    mChildCheckStates.put(contactName, mGetChecked);

    if (convertView == null) {

        LayoutInflater inflater = (LayoutInflater) mContext
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = inflater.inflate(R.layout.contact_name_item, null);

        groupViewHolder = new GroupViewHolder();

        groupViewHolder.mContactName = (TextView) convertView
                .findViewById(R.id.lblListHeader);

        groupViewHolder.mContactImage = (ImageView) convertView
                .findViewById(R.id.ivContactPhoto);

        convertView.setTag(groupViewHolder);
    } else {

        groupViewHolder = (GroupViewHolder) convertView.getTag();
    }

    if (contactImage != null) {
        groupViewHolder.mContactImage.setImageBitmap(contactImage);

    } else {
        groupViewHolder.mContactImage
                .setImageResource(R.drawable.default_contact);
    }

    groupViewHolder.mContactName.setText(contactName);

    return convertView;
}

@Override
public int getChildrenCount(int groupPosition) {
    return mListDataChild.get(mListDataHeader.get(groupPosition).getName())
            .size();
}

@Override
public ContactPhoneItems getChild(int groupPosition, int childPosition) {
    return mListDataChild.get(mListDataHeader.get(groupPosition).getName())
            .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 String contactName = getGroup(groupPosition).getName();
    final int mChildPosition = childPosition;

    numberText = getChild(groupPosition, childPosition).getNumber();
    String typeText = getChild(groupPosition, childPosition).getPhoneType();

    mGetChecked = mChildCheckStates.get(contactName);

    if (convertView == null) {

        LayoutInflater inflater = (LayoutInflater) this.mContext
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = inflater.inflate(R.layout.contact_detail_item, null);

        childViewHolder = new ChildViewHolder();

        childViewHolder.mPhoneNumber = (TextView) convertView
                .findViewById(R.id.tv_phone_number);

        childViewHolder.mPhoneType = (TextView) convertView
                .findViewById(R.id.tv_phone_type);

        childViewHolder.mCheckBox = (CheckBox) convertView
                .findViewById(R.id.checkBox);

        convertView.setTag(R.layout.contact_detail_item, childViewHolder);

    } else {

        childViewHolder = (ChildViewHolder) convertView
                .getTag(R.layout.contact_detail_item);
    }

    childViewHolder.mPhoneNumber.setText(numberText);
    childViewHolder.mPhoneType.setText(typeText);

    childViewHolder.mCheckBox.setChecked(mGetChecked[mChildPosition]);
    childViewHolder.mCheckBox.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {

            boolean isChecked = childViewHolder.mCheckBox.isChecked();

            Log.d("Debug", "isChecked = " + String.valueOf(isChecked));

            if (isChecked) {

                selectedNumbers.add(numberText);

            } else {

                selectedNumbers.remove(numberText);
            }

            childViewHolder.mCheckBox.setChecked(isChecked);

            mGetChecked[mChildPosition] = isChecked;
            mChildCheckStates.put(contactName, mGetChecked);
        }
    });

    return convertView;
}

@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
    return false;
}

@Override
public boolean hasStableIds() {
    return false;
}

public ArrayList<String> getSelectedNumbers() {

    return selectedNumbers;

}

public final class GroupViewHolder {

    TextView mContactName;
    ImageView mContactImage;
}

public final class ChildViewHolder {

    TextView mPhoneNumber;
    TextView mPhoneType;
    CheckBox mCheckBox;
}

@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    // TODO Auto-generated method stub
    Log.d("Debug", "onCheckChangedListener : " + String.valueOf(isChecked));
}

}

Goniometer answered 1/2, 2014 at 4:22 Comment(0)
C
2

I see you have a selectedNumbers object, what type of collection is that? It might make most sense to have a Map or List or some collection based on something more unique, like the group & child position. Then you can do this:

(EDIT: edited the code to make sure the checkedChangedListener doesn't trigger) (EDIT 2 based on OP's update): You probably shouldn't use a map of Strings to boolean arrays. Instead try this. It's fairly similar but uses ints which will be more foolproof to compare than Strings

...
HashMap<Integer, Integer> mCheckedStates; // assume initialized as you did
...

childViewHolder.mPhoneNumber.setText(numberText);
childViewHolder.mPhoneType.setText(typeText);
childViewHolder.mCheckBox.setOnCheckedChangeListener(null);
if (mCheckedStates.contains(groupPosition)) {
    int checkedChildren = mCheckedStates.get(groupPosition);
    if ((checkedChildren & (1 << childPosition)) ==  1) {
        childViewHolder.mCheckBox.setChecked(true);
    } else {
        //Edit: Cannot rely on checkbox being false on Recycle,
        childViewHolder.mCheckBox.setChecked(false);
    }
}
childViewHolder.mCheckBox.setOnCheckedChangeListener(this);

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

    // get group and child positions
    if (isChecked) {

        if (mCheckedStates.contains(groupPosition)) {
            // I'm using a bitmap to keep resources low, but it's not necessary to do it this way
            int childmap = mCheckedStates.get(groupPosition);
            childmap += (1 << childPosition);
            mCheckedStates.put(groupPosition, childmap);
        } else {
            mCheckedStates.put(groupPosition, (1 << childPosition));
        }
    } else {
        if (mCheckedStates.contains(groupPosition)) {
            int childmap = mCheckedStates.get(groupPosition);
            if (childmap == (1 << childPosition)) {
                mCheckedStates.remove(groupPosition);
            } else {
                childmap &= ~(1 << childPosition);
                mCheckedStates.put(groupPosition, childmap);
            }
        }
    }
}

}

Cowbird answered 1/2, 2014 at 4:57 Comment(10)
I updated the OP. selectedNumbers is an array of ints that is a collection of the numbers that are checked off.Goniometer
I tried with a HashMap<String, boolean[]> (String = groupPosition, booelan = check state) but when I expand a group and check a box then expand another group, the first box unchecksGoniometer
I do not see any code in your getChildView function to set the correct checked state as the views recycle. You setText, but not checked state.Bash
As @user3126670 and I mention, please try storing the actual value of the checked state, being careful of re-triggering the check change listenerCowbird
Just made some changes based on what you said. Testing it out now. Will update OP with result in 2 or 3 minutes... OP updatedGoniometer
I have one critique of the above solution though, it only sets the check state when (true). It needs to be set regardless, as we can't rely on the recycled state being false.Bash
@user3126670 thanks, I've updated my solution with that suggestion.Cowbird
@Cowbird I never got a notify that you updated your answer. That solution looks really interesting. I'm going to give it a go now but have to leave for work soon so may not be able to update until tonightGoniometer
@Cowbird I'm really sorry to say that it didn't do it. still getting random other boxes checked when I get oneGoniometer
@Cowbird I'm giving you the check because although your answer didn't work for me, it pointed in the direction that ended up doing the trick, which I'm posting below. Thanks a ton!Goniometer
G
12

HERE IS THE WORKING SOLUTION

OK, although there seems to be a lot of suggestions on the internet about checkboxes in expandable listviews, none had worked for me. I was able to get it working with the help of a bunch of people, especially anddev84. Now I'm not going to claim that this is perfect or foolproof. I'm just claiming that it works for me. I've tested on 2 devices and I'm very pleased with it.

So I've taken my working code, dwindled it down to its essential parts and added a bunch of helpful comments so anybody who needs it can you it. Hopefully it works as well for you as it does for me. Enjoy

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 MyExpandableListAdapter 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 MyExpandableListAdapter(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 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).getGroupText();

        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 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;
    }
}
Goniometer answered 2/2, 2014 at 1:49 Comment(2)
Thanks for sharing the code. I was really stuck at this for long time. A quick question - How do we access the list of checked items from the main activity ? Thanks.Leontina
If your activity is the one that holds the adapter reference, you can just create a getter in the adapter that returns the check box array and call myAdapeter.myGetter() from the activityGoniometer
C
2

I see you have a selectedNumbers object, what type of collection is that? It might make most sense to have a Map or List or some collection based on something more unique, like the group & child position. Then you can do this:

(EDIT: edited the code to make sure the checkedChangedListener doesn't trigger) (EDIT 2 based on OP's update): You probably shouldn't use a map of Strings to boolean arrays. Instead try this. It's fairly similar but uses ints which will be more foolproof to compare than Strings

...
HashMap<Integer, Integer> mCheckedStates; // assume initialized as you did
...

childViewHolder.mPhoneNumber.setText(numberText);
childViewHolder.mPhoneType.setText(typeText);
childViewHolder.mCheckBox.setOnCheckedChangeListener(null);
if (mCheckedStates.contains(groupPosition)) {
    int checkedChildren = mCheckedStates.get(groupPosition);
    if ((checkedChildren & (1 << childPosition)) ==  1) {
        childViewHolder.mCheckBox.setChecked(true);
    } else {
        //Edit: Cannot rely on checkbox being false on Recycle,
        childViewHolder.mCheckBox.setChecked(false);
    }
}
childViewHolder.mCheckBox.setOnCheckedChangeListener(this);

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

    // get group and child positions
    if (isChecked) {

        if (mCheckedStates.contains(groupPosition)) {
            // I'm using a bitmap to keep resources low, but it's not necessary to do it this way
            int childmap = mCheckedStates.get(groupPosition);
            childmap += (1 << childPosition);
            mCheckedStates.put(groupPosition, childmap);
        } else {
            mCheckedStates.put(groupPosition, (1 << childPosition));
        }
    } else {
        if (mCheckedStates.contains(groupPosition)) {
            int childmap = mCheckedStates.get(groupPosition);
            if (childmap == (1 << childPosition)) {
                mCheckedStates.remove(groupPosition);
            } else {
                childmap &= ~(1 << childPosition);
                mCheckedStates.put(groupPosition, childmap);
            }
        }
    }
}

}

Cowbird answered 1/2, 2014 at 4:57 Comment(10)
I updated the OP. selectedNumbers is an array of ints that is a collection of the numbers that are checked off.Goniometer
I tried with a HashMap<String, boolean[]> (String = groupPosition, booelan = check state) but when I expand a group and check a box then expand another group, the first box unchecksGoniometer
I do not see any code in your getChildView function to set the correct checked state as the views recycle. You setText, but not checked state.Bash
As @user3126670 and I mention, please try storing the actual value of the checked state, being careful of re-triggering the check change listenerCowbird
Just made some changes based on what you said. Testing it out now. Will update OP with result in 2 or 3 minutes... OP updatedGoniometer
I have one critique of the above solution though, it only sets the check state when (true). It needs to be set regardless, as we can't rely on the recycled state being false.Bash
@user3126670 thanks, I've updated my solution with that suggestion.Cowbird
@Cowbird I never got a notify that you updated your answer. That solution looks really interesting. I'm going to give it a go now but have to leave for work soon so may not be able to update until tonightGoniometer
@Cowbird I'm really sorry to say that it didn't do it. still getting random other boxes checked when I get oneGoniometer
@Cowbird I'm giving you the check because although your answer didn't work for me, it pointed in the direction that ended up doing the trick, which I'm posting below. Thanks a ton!Goniometer
A
2

I was also the victim of the same problem and tried your solution and now its working like a charm. Thanks buddy. :)

I just modified the code with String instead of custom domain model for the ease of other people.

MainActivity.java

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import android.app.Activity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.OnChildClickListener;
import android.widget.Toast;

public class MainActivity extends Activity {

List<String> groupList;
List<String> childList;
Map<String, List<String>> laptopCollection;
ExpandableListView expListView;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Log.d("-Test Exapansion-", "Nai Yara");

    createGroupList();

    createCollection();

    expListView = (ExpandableListView) findViewById(R.id.laptop_list);

 final MyExpandableListAdapter expListAdapter = new  MyExpandableListAdapter
(getApplicationContext() , 
groupList, laptopCollection);
    expListView.setAdapter(expListAdapter);
    expListView.getHeaderViewsCount();

    //expListView.expandGroup(1);
    //expListView.expandGroup(2);

int numberOfHeaders = expListView.getExpandableListAdapter().getGroupCount();
    Log.d("-Test Exapansion-", "Number of Groups are:"+numberOfHeaders);

    for(int i = 0; i<numberOfHeaders; i++){
        expListView.expandGroup(i);
    }

    setGroupIndicatorToRight();

    expListView.setOnChildClickListener(new OnChildClickListener() {

        public boolean onChildClick(ExpandableListView parent, View v,
                int groupPosition, int childPosition, long id) {
            final String selected = (String) expListAdapter.getChild(
                    groupPosition, childPosition);
        Toast.makeText(getBaseContext(), selected, Toast.LENGTH_LONG)
                    .show();

            return true;
        }
    });
}

private void createGroupList() {
    groupList = new ArrayList<String>();
    groupList.add("HP");
    groupList.add("Dell");
    groupList.add("Lenovo");
    groupList.add("Sony");
    groupList.add("HCL");
    groupList.add("Samsung");
    groupList.add("HTC");
}

private void createCollection() {
    // preparing laptops collection(child)
    String[] hpModels = { "HP Pavilion G6-2014TX", "ProBook HP 4540",
            "HP Envy 4-1025TX" };
    String[] hclModels = { "HCL S2101", "HCL L2102", "HCL V2002" };
    String[] lenovoModels = { "IdeaPad Z Series", "Essential G Series",
            "ThinkPad X Series", "Ideapad Z Series" };
    String[] sonyModels = { "VAIO E Series", "VAIO Z Series",
            "VAIO S Series", "VAIO YB Series" };
    String[] dellModels = { "Inspiron", "Vostro", "XPS" };
    String[] samsungModels = { "NP Series", "Series 5", "SF Series" };
    String[] hTcModels = { "One", "One X", "Desire Series" };

    laptopCollection = new LinkedHashMap<String, List<String>>();

    for (String laptop : groupList) {
        if (laptop.equals("HP")) {
            loadChild(hpModels);
        } else if (laptop.equals("Dell"))
            loadChild(dellModels);
        else if (laptop.equals("Sony"))
            loadChild(sonyModels);
        else if (laptop.equals("HCL"))
            loadChild(hclModels);
        else if (laptop.equals("Samsung"))
            loadChild(samsungModels);
        else if (laptop.equals("HTC"))
            loadChild(hTcModels);
        else
            loadChild(lenovoModels);

        laptopCollection.put(laptop, childList);
    }
}

private void loadChild(String[] laptopModels) {
    childList = new ArrayList<String>();
    for (String model : laptopModels)
        childList.add(model);
}

private void setGroupIndicatorToRight() {
    /* Get the screen width */
    DisplayMetrics dm = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(dm);
    int width = dm.widthPixels;

    expListView.setIndicatorBounds(width - getDipsFromPixel(35), width
            - getDipsFromPixel(5));
}

// Convert pixel to dip
public int getDipsFromPixel(float pixels) {
    // Get the screen's density scale
    final float scale = getResources().getDisplayMetrics().density;
    // Convert the dps to pixels, based on density scale
    return (int) (pixels * scale + 0.5f);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    //getMenuInflater().inflate(R.menu.activity_main, menu);
    return true;
}
}

MyExpandableListAdapter.java

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import android.app.Activity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.OnChildClickListener;
import android.widget.Toast;

public class MainActivity extends Activity {

List<String> groupList;
List<String> childList;
Map<String, List<String>> laptopCollection;
ExpandableListView expListView;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Log.d("-Test Exapansion-", "Nai Yara");

    createGroupList();

    createCollection();

expListView = (ExpandableListView) findViewById(R.id.laptop_list);
final MyExpandableListAdapter 
expListAdapter = 
new MyExpandableListAdapter(getApplicationContext()
, groupList, laptopCollection);
    expListView.setAdapter(expListAdapter);
    expListView.getHeaderViewsCount();



int numberOfHeaders = expListView.getExpandableListAdapter().getGroupCount();
    Log.d("-Test Exapansion-", "Number of Groups are:"+numberOfHeaders);

    for(int i = 0; i<numberOfHeaders; i++){
        expListView.expandGroup(i);
    }

    setGroupIndicatorToRight();

    expListView.setOnChildClickListener(new OnChildClickListener() {

        public boolean onChildClick(ExpandableListView parent, View v,
                int groupPosition, int childPosition, long id) {
            final String selected = (String) expListAdapter.getChild(
                    groupPosition, childPosition);
        Toast.makeText(getBaseContext(), selected, Toast.LENGTH_LONG)
                    .show();

            return true;
        }
    });
}

private void createGroupList() {
    groupList = new ArrayList<String>();
    groupList.add("HP");
    groupList.add("Dell");
    groupList.add("Lenovo");
    groupList.add("Sony");
    groupList.add("HCL");
    groupList.add("Samsung");
    groupList.add("HTC");
}

private void createCollection() {
    // preparing laptops collection(child)
    String[] hpModels = { "HP Pavilion G6-2014TX", "ProBook HP 4540",
            "HP Envy 4-1025TX" };
    String[] hclModels = { "HCL S2101", "HCL L2102", "HCL V2002" };
    String[] lenovoModels = { "IdeaPad Z Series", "Essential G Series",
            "ThinkPad X Series", "Ideapad Z Series" };
    String[] sonyModels = { "VAIO E Series", "VAIO Z Series",
            "VAIO S Series", "VAIO YB Series" };
    String[] dellModels = { "Inspiron", "Vostro", "XPS" };
    String[] samsungModels = { "NP Series", "Series 5", "SF Series" };
    String[] hTcModels = { "One", "One X", "Desire Series" };

    laptopCollection = new LinkedHashMap<String, List<String>>();

    for (String laptop : groupList) {
        if (laptop.equals("HP")) {
            loadChild(hpModels);
        } else if (laptop.equals("Dell"))
            loadChild(dellModels);
        else if (laptop.equals("Sony"))
            loadChild(sonyModels);
        else if (laptop.equals("HCL"))
            loadChild(hclModels);
        else if (laptop.equals("Samsung"))
            loadChild(samsungModels);
        else if (laptop.equals("HTC"))
            loadChild(hTcModels);
        else
            loadChild(lenovoModels);

        laptopCollection.put(laptop, childList);
    }
}

private void loadChild(String[] laptopModels) {
    childList = new ArrayList<String>();
    for (String model : laptopModels)
        childList.add(model);
}

private void setGroupIndicatorToRight() {
    /* Get the screen width */
    DisplayMetrics dm = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(dm);
    int width = dm.widthPixels;

    expListView.setIndicatorBounds(width - getDipsFromPixel(35), width
            - getDipsFromPixel(5));
}

// Convert pixel to dip
public int getDipsFromPixel(float pixels) {
    // Get the screen's density scale
    final float scale = getResources().getDisplayMetrics().density;
    // Convert the dps to pixels, based on density scale
    return (int) (pixels * scale + 0.5f);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    //getMenuInflater().inflate(R.menu.activity_main, menu);
    return true;
}
}
Ashbey answered 15/5, 2014 at 12:45 Comment(0)
H
0

The problem is you use positions as ids. Position is not a id. Position may change and position of a child without group position is not unique identifier.

Hydromel answered 2/2, 2014 at 9:28 Comment(0)
M
0
  1. First You Have To Create New HashMap To Maintain The Child CheckBoxState in ExpandablelistView

  2. Declare HashMap In Adapter As Like-

    private HashMap mChildCheckStates;// New HashMap Created

  3. Following Code Past In Your Adapter

    cb.setOnCheckedChangeListener(null);
    
    if (mChildCheckStates.containsKey(mGroupPosition)) {
    
        boolean getChecked[] = mChildCheckStates.get(mGroupPosition);
         cb.setChecked(getChecked[mChildPosition]);
    
    
    }else {
    
        boolean getChecked[] = new boolean[getChildrenCount(mGroupPosition)];
    
        mChildCheckStates.put(mGroupPosition, getChecked);
    
       cb.setChecked(false);
    
    
    }
    
     cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
    
         @Override
         public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    
             if (isChecked) {
    
                 boolean getChecked[] = mChildCheckStates.get(mGroupPosition);
                 getChecked[mChildPosition] = isChecked;
                 mChildCheckStates.put(mGroupPosition, getChecked);
    
                 //Toast.makeText(mContext,"group-"+mGroupPosition+"child-"+mChildPosition,Toast.LENGTH_SHORT).show();
                // String value=mListDataChild[0];
    
                 Toast.makeText(mContext,"-"+mListDataChild,Toast.LENGTH_SHORT).show();
    
    
             } else {
    
                 boolean getChecked[] = mChildCheckStates.get(mGroupPosition);
                 getChecked[mChildPosition] = isChecked;
                 mChildCheckStates.put(mGroupPosition, getChecked);
                 Toast.makeText(mContext,"group-"+mGroupPosition+"child-"+mChildPosition,Toast.LENGTH_SHORT).show();
             }
         }
     });
    
  4. cb is checkbox.

I Hope This Logic I Help To AnyOne Thanks

Misapply answered 3/9, 2016 at 11:22 Comment(1)
cb.setOnCheckedChangeListener(null); is no t null is my mistake to write above if , else condition code are under the this cb.setOnCheckedChangeListener { ....Use Above First if,els condition inside........ }Misapply

© 2022 - 2024 — McMap. All rights reserved.