Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android ExpandableListView with Checkbox, Controlling checked state

Friends,

I am trying to write a application which use checkbox in ExpandableListView, I got a problem here which is maintaining checkbox state of the application, I got the example from here , my problem is maintaining checked state of the checkboxes, whenever I check one of the checkboxes and Expand the List, the checkboxes do not have the checked state where they supposed to have. I have try to maintain by adding ArrayList to store the position of the store and reload the position in getChildView() but still not achieve what I want to do. here is my code

public class ElistCBox extends ExpandableListActivity {

    private static final String LOG_TAG = "ElistCBox";
    ArrayList<String > chkState = new ArrayList<String>();
    static final String colors[] = {"grey","blue","yellow","red"};
    static final String shades[][] ={ { "lightgrey","#D3D3D3","dimgray","#696969", "sgi >gray 92","#EAEAEA"},
        { "dodgerblue 2","#1C86EE","steelblue >2","#5CACEE","powderblue","#B0E0E6"},
        { "yellow 1","#FFFF00", "gold 1","#FFD700","darkgoldenrod 1"," #FFB90F" },
        {"indianred 1","#FF6A6A", "firebrick 1","#FF3030", "maroon","#800000" } };

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle icicle)
    {
        super.onCreate(icicle);
        setContentView(R.layout.main); 
        SimpleExpandableListAdapter expListAdapter = new SimpleExpandableListAdapter( 
            this,
            createGroupList(),
            R.layout.group_row, new String[] { "colorName" },
            new int[] { R.id.childname }, createChildList(),
            R.layout.child_row,
            new String[] { "shadeName", "rgb" },
            new int[] { R.id.childname, R.id.rgb }
        ) { 
            @Override public View getChildView(final int groupPosition, final int childPosition, boolean isLastChild, View convertView, ViewGroup parent)
            {
                final View v = super.getChildView(groupPosition, childPosition, isLastChild, convertView, parent);

                final CheckBox chkColor = (CheckBox)v.findViewById(R.id.check1);

                if(chkState.contains(groupPosition+","+childPosition)){
                    chkColor.setChecked(true);
                }else{
                    chkColor.setChecked(false);
                }
                chkColor.setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Log.e("Checkbox Onclick Listener", Integer.toString(groupPosition) + " - " + Integer.toString(childPosition));
                    }
                });
                chkColor.setOnCheckedChangeListener(new OnCheckedChangeListener() {
                    @Override
                    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                        Log.e("Checkbox check change Listener", Integer.toString(groupPosition) + " - " + Integer.toString(childPosition));
                        if(chkColor.isChecked()){
                            chkState.add(groupPosition+","+childPosition);
                        } else {
                            chkState.remove(groupPosition+","+childPosition); 
                        }
                    }
                });
                return super.getChildView(groupPosition, childPosition, isLastChild, convertView, parent);
            }
        };
        setListAdapter( expListAdapter );
    }

    public void onContentChanged  () {
        super.onContentChanged();
        Log.e( LOG_TAG, "onContentChanged" );
    }

    public boolean onChildClick(
            ExpandableListView parent, 
            View v, 
            int groupPosition,
            int childPosition,
            long id) {
        Log.e( LOG_TAG, "onChildClick: "+childPosition );
        CheckBox cb = (CheckBox)v.findViewById( R.id.check1 );
        if( cb != null )
        cb.toggle();
        return false;
    }

    public void  onGroupExpand  (int groupPosition) {
        Log.e( LOG_TAG,"onGroupExpand: "+groupPosition );
    }

    private List createGroupList() {
        ArrayList result = new ArrayList();
        for( int i = 0 ; i < colors.length ; ++i ) {
            HashMap m = new HashMap();
            m.put( "colorName",colors[i] );
            result.add( m );
        }
        return (List)result; 
    }

    private List createChildList() {
        ArrayList result = new ArrayList();
        for( int i = 0 ; i < shades.length ; ++i ) {
            ArrayList secList = new ArrayList();
            for( int n = 0 ; n < shades[i].length; n += 2 ) {
                HashMap child = new HashMap();
                child.put( "shadeName", shades[i][n] );
                child.put( "rgb", shades[i][n+1] ); secList.add( child );
            }
            result.add( secList );
        } 
        return result;
    }
}
like image 617
NyanLH Avatar asked Feb 21 '11 16:02

NyanLH


2 Answers

ExpandableListView with multiselect. Tested on API 4.3 and 4.0.3 This code also correctly handles changing screen orientation. Blocking groups made ​​to work properly with the selected elements through SparseBooleanArray.

I hope this sample code will help :)

Activity

ExpandableListView list;
ArrayList<YouCat> cat = new ArrayList<YouCat>();
private YourAdapter mAdapter;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.layout);
    list = (ExpandableListView)findViewById(R.id.list);
    mAdapter = new YourAdapter(this, list, cat);
    if(savedInstanceState == null)
        //collect your data
    list.setAdapter(mAdapter);
    list.setItemsCanFocus(false);
    list.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE);
    list.setOnChildClickListener(this);
    list.setOnGroupClickListener(this);
}

public boolean onGroupClick(ExpandableListView parent, View v,
            int groupPosition, long id) {
        return cat.get(groupPosition).selected;
}

public boolean onChildClick(ExpandableListView parent, View v,
            int groupPosition, int childPosition, long id) {
        YouCat cat = new YouCat();
        YouSubCat subcat = new YouSubCat();
        subcat = cat.get(groupPosition).sub.get(childPosition);
        subcat.selected = !cat.get(groupPosition).sub.get(childPosition).selected;
        cat.get(groupPosition).sub.set(childPosition, subcat);

        boolean isGroupHasSelected = false;
        for(int i = 0; i < cat.get(groupPosition).sub.size() && !isGroupHasSelected; i ++){
            isGroupHasSelected = cat.get(groupPosition).sub.get(i).selected;
        }
        cat = cat.get(groupPosition);
        cat.selected = isGroupHasSelected;
        cat.set(groupPosition, cat);
        //mAdapter.notifyDataSetChanged();

        int position = parent.getFlatListPosition(ExpandableListView.getPackedPositionForChild(groupPosition, childPosition));
        parent.setItemChecked(position, subcat.selected);

        return true;
}

 public void onRestoreInstanceState(Bundle savedInstanceState) {
     super.onRestoreInstanceState(savedInstanceState);
     //restore data 
     cat = (ArrayList<YouCat>) savedInstanceState.getSerializable("cat");
     Type selType = new TypeToken<SparseBooleanArray>() {}.getType();
     SparseBooleanArray checked = new Gson().fromJson(savedInstanceState.getString("sel"), selType);
     //set new data to adapter and refresh
     mAdapter.refreshList(cat);
 }


 public void onSaveInstanceState(Bundle savedInstanceState) {
     super.onSaveInstanceState(savedInstanceState);
     //save data and selection from list to bundle
     savedInstanceState.putSerializable("cat", cat);
     savedInstanceState.putString("sel", new Gson().toJson(list.getCheckedItemPositions()).toString());
 }

Adapter

public class YouAdapter extends BaseExpandableListAdapter{

    private Context context;
    private List<YouCat> mGroupCollection;
    private ExpandableListView mExpandableListView;

    public YouAdapter(Context context, ExpandableListView pExpandableListView,
            List<YouCat> pGroupCollection) {
        this.context = context;
        this.mGroupCollection = pGroupCollection;
        this.mExpandableListView = pExpandableListView;
    }

    @Override
    public Object getChild(int groupPosition, int childPosition) {
        return mGroupCollection.get(groupPosition).sub.get(childPosition).name;
    }

    @Override
    public long getChildId(int groupPosition, int childPosition) {
        return childPosition;
    }

    class ChildHolder {
    CheckBox checkBox;
    TextView name, desc;
   }

    @Override
    public View getChildView(int groupPosition, int childPosition,
            boolean isLastChild, View convertView, ViewGroup parent) {
        ChildHolder childHolder;
        if( convertView == null ){
            convertView = LayoutInflater.from(context).inflate(R.layout.childrow, null);
            childHolder = new ChildHolder();
            childHolder.checkBox = (CheckBox) convertView.findViewById(R.id.myCheckBox);
            childHolder.name=(TextView)convertView.findViewById(R.id.textView1);
            childHolder.desc=(TextView)convertView.findViewById(R.id.textView2);
            convertView.setTag(childHolder);
       }else{
            childHolder = (ChildHolder) convertView.getTag(); 
       }
        childHolder.name.setText(mGroupCollection.get(groupPosition).sub.get(childPosition).name);
        childHolder.desc.setText(mGroupCollection.get(groupPosition).sub.get(childPosition).desc);
        childHolder.checkBox.setChecked(mGroupCollection.get(groupPosition).sub.get(childPosition).selected);

        return convertView;
    }

    @Override
    public int getChildrenCount(int groupPosition) {
        return mGroupCollection.get(groupPosition).sub.size();
    }

    @Override
    public Object getGroup(int groupPosition) {
        return mGroupCollection.get(groupPosition);
    }

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

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

    class GroupHolder {
      TextView title;
   }

    @Override
    public View getGroupView(int groupPosition, boolean isExpanded,
            View convertView, ViewGroup parent) {
        GroupHolder groupHolder;
       if( convertView == null ){
            convertView = LayoutInflater.from(context).inflate(R.layout.grouplayout,null);
            groupHolder = new GroupHolder();
            groupHolder.title = (TextView)convertView.findViewById( R.id.text1 );
            convertView.setTag(groupHolder);
       }else{
            groupHolder = (GroupHolder) convertView.getTag(); 
       }
       groupHolder.title.setText(mGroupCollection.get(groupPosition).name);
       return convertView;
    }

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

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

    public void refreshList(List<YouCat> collection){
        mGroupCollection = collection;
        notifyDataSetChanged();
        for(int g = 0; g < mGroupCollection.size(); g ++){
            if(mGroupCollection.get(g).selected)
                mExpandableListView.expandGroup(g);
            else
                mExpandableListView.collapseGroup(g);
        }
    }

}

YouCat class

public class YouCat implements Serializable {
        private static final long serialVersionUID = 2070450081971040619L;
        public String name = null;
        public boolean selected = false;
        public ArrayList<YouSubCat> sub = new ArrayList<YouSubCat>();
}

YouSubCat class

public class YouSubCat implements Serializable {
    private static final long serialVersionUID = -1487507723105914936L;
    public String name = null, desc = null;
    public boolean selected = false;
}

Child row layout

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

    <CheckBox
        android:id="@+id/myCheckBox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:clickable="false"
        android:focusable="false" />

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="10dp"
        android:layout_toRightOf="@+id/myCheckBox"
        android:text="TextView"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/textView1"
        android:layout_alignParentRight="true"
        android:layout_below="@+id/textView1"
        android:text="TextView" />

</RelativeLayout>

Group layout

<?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="wrap_content"
    android:orientation="vertical"
    android:padding="8dp" >

    <TextView
        android:id="@+id/text1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="?android:attr/expandableListPreferredItemPaddingLeft"
        android:textAppearance="?android:attr/textAppearanceMedium" />

</LinearLayout>

Get selected child items

final SparseBooleanArray checkedItems = list.getCheckedItemPositions();
for (int i = 0; i < checkedItems.size(); i++) {
  if(checkedItems.valueAt(i))
    data = ((String)list.getItemAtPosition(checkedItems.keyAt(i)));
}
like image 140
pwb Avatar answered Oct 05 '22 23:10

pwb


this is my answer to the this issue, u need to keep a check state for individual child and check on it when the the child is clicked. after some research, this is due to android's view lifecycle causing the view to refresh thus not keeping the state.

public class MyExpandableListAdapter extends BaseExpandableListAdapter {
//Variables
// Sample data set.  children[i] contains the children (String[]) for groups[i].
private String[] groups = { "People Names", "Dog Names", "Cat Names", "Fish Names" }; //headers
private String[][] children = {
        { "Arnold", "Barry", "Chuck", "David" },
        { "Ace", "Bandit", "Cha-Cha", "Deuce" },
        { "Fluffy", "Snuggles" },
        { "Goldy", "Bubbles" }
};
private String[] group_vaues = {"PN", "DN", "CN", "FN"};
private String[][] children_values = {
        { "Ar", "Ba", "Ch", "Da" },
        { "Ace", "Ban", "Cha", "Deu" },
        { "Flu", "Snu" },
        { "Gol", "Bub" }
};
ArrayList<ArrayList<Integer>> check_states = new ArrayList<ArrayList<Integer>>();

private Context context;

//Constructors
public MyExpandableListAdapter() {

}

public MyExpandableListAdapter(Context c) {
    this.context = c;
}

//Set Methods
public void setGroupsAndValues(String[] g, String[] v) {
    this.groups = g;
    this.group_vaues = v;
}

public void setChildrenAndValues(String[][] c, String[][] v) {
    this.children = c;
    this.children_values = v;
    //initialize the states to all 0;
    for(int i = 0; i < c.length; i++) {
        ArrayList<Integer> tmp = new ArrayList<Integer>();
        for(int j = 0; j < c[i].length; j++) {
            tmp.add(0);
        }
        check_states.add(tmp);
    }
}

//Get Methods
public Object getChild(int groupPosition, int childPosition) {
    return children[groupPosition][childPosition];
}

public long getChildId(int groupPosition, int childPosition) {
    return childPosition;
}

public int getChildrenCount(int groupPosition) {
    return children[groupPosition].length;
}

public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
        View convertView, ViewGroup parent) {
    View grid;


   LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
   grid = inflater.inflate(R.layout.specialty_list_item, parent, false);

   final int grpPos = groupPosition;
   final int childPos = childPosition;

    TextView header = (TextView)grid.findViewById(R.id.title);
    header.setText(getChild(groupPosition, childPosition).toString());
    final View tick = grid.findViewById(R.id.image);
    if(check_states.get(grpPos).get(childPos) == 1)
        tick.setVisibility(View.VISIBLE);
    else
        tick.setVisibility(View.GONE);

    grid.setOnClickListener(new OnClickListener(){
        @Override
        public void onClick(View v) {
            check_states.get(grpPos).set(childPos, 1);
            tick.setVisibility(View.VISIBLE);
        }
    });

    return grid;
}

public Object getGroup(int groupPosition) {
    return groups[groupPosition];
}

public int getGroupCount() {
    return groups.length;
}

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

public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
        ViewGroup parent) {
    View grid;

    if(convertView==null){
       grid = new View(context);
       LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
       grid = inflater.inflate(R.layout.specialty_header, parent, false);
    }else{
       grid = (View)convertView;
    }

    TextView header = (TextView)grid.findViewById(R.id.specialty_header);
    header.setText(getGroup(groupPosition).toString());


    return grid;
}

public boolean isChildSelectable(int groupPosition, int childPosition) {
    return true;
}

public boolean hasStableIds() {
    return true;
}

}

like image 21
Jianhong Avatar answered Oct 05 '22 22:10

Jianhong