Search code examples
androidandroid-listviewandroid-arrayadapterandroid-checkbox

CheckBox in ListView being reset when it leaves the screen


I have followed the tutorial here to create a custom ListView that shows items with category headers. I have modified the list_item_entry.xml to put a CheckBox in the item:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:gravity="center_vertical"
    android:paddingRight="?android:attr/scrollbarSize" >

    <CheckBox
        android:id="@+id/option_checkbox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingLeft="6dp"
        android:focusable="false"
        android:clickable="false" />

    <TextView
        android:id="@+id/list_item_entry_title"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:singleLine="true"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:gravity="center_vertical"
        android:minHeight="?android:attr/listPreferredItemHeightSmall"
        android:ellipsize="marquee"
        android:fadingEdge="horizontal" />

</LinearLayout>

My problem is that if I check some of the CheckBoxes then scroll them off the screen, when they come back they are unchecked. However listView.getCheckedItemPositions() still shows that the item is checked.

I'm pretty sure that my problem is with the getView() method in my custom ArrayAdapter:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    final Item i = items.get(position);
    if (i != null) {
        if(i.isSection()){
            SectionItem si = (SectionItem)i;
            convertView = vi.inflate(R.layout.list_item_section, parent, false);

            convertView.setOnClickListener(null);
            convertView.setOnLongClickListener(null);
            convertView.setLongClickable(false);

            final TextView sectionView =
                     (TextView) convertView.findViewById(R.id.list_item_section_text);
            sectionView.setText(si.getTitle());
        }else{
            EntryItem ei = (EntryItem)i;
            convertView = vi.inflate(R.layout. list_item_entry, parent, false);
            final TextView title =
                     (TextView) convertView.findViewById(R.id.list_item_entry_title);
            if (title != null) 
                title.setText(ei.getTitle());
        }
    }
    return convertView;
 }

I think that I have two issues here, though I have no idea how to solve either:

  1. Using vi.inflate every time is causing android to constantly create views which is bad (not sure about this). I tried to only inflate it if convertView == null but then sometimes convertView would be in the wrong format, ie. List_item_section when it should be List_item_entry. Is it fine to inflate it everytime?

  2. I think that inflating the view each time is causing the CheckBoxes to be reset, although I may be wrong about this.

So how do I make it so the CheckBoxes will stay checked when the leave and return to the screen? And will this method fill Android's memory with Views if the the list is sufficiently long?

Update: I liked @user3815165's answer because I didn't need to store the checked value for a sectionItem which doesn't have a checkbox. But as I mentioned in a comment, since the items list is not in the context of the Activity then the values of whether each EntryItem is checked or not persists when the view is destroyed and creates bugs.

So I decided to go with @Palash's answer, even though it stored data not needed (only a single boolean value for each SectionItem in the list). It works perfectly.


Solution

  • you need to maintain a status array of type boolean in your activity, pass that array into your list adapter and while setting the checkbox check status of that position, also you need to update that status array likewise on click event of checkbox. try this you will get the desired output.

    //While Setting the checkbox in adapter
    
         if(bStatus[position]==false)
            {
                 itemSet.chSelectItem.setChecked(false);
            }else if(bStatus[position]==true)
                {
                  itemSet.chSelectItem.setChecked(true);
                }   
    

    In your main Activity

          //initilize Arraylist in main Activity
            boolean[] bStatus;
            bStatus = new boolean[BeanArray.size()];
            Arrays.fill(bStatus, false);
        MyAdapter adapter = new MyAdapter(this, BeanArray, bStatus);
    listView.setAdapter(adapter);