0

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.

Yep_It's_Me
  • 4,494
  • 4
  • 43
  • 66

2 Answers2

0

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);
0
class Item{
 boolean isSection;
 String title;
 boolean isOptionChecbox;

 //your getter/setter

    @Override
    public String toString() {
        return title;
    }
}

you Adapter:
public class listAdapter extends ArrayAdapter<Item> {

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    final Item i = items.get(position);
    if(i.isSection()){
              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{
              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());

              CheckBox optionCheckbox = (CheckBox) convertView.findViewById(R.id.option_checkbox);
              optionCheckbox.setChecked(ei.isOptionCheckbox());
              optionCheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                  @Override
                  public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
                      item.setOptionCheckbox(b);
                  }
            });
       }      
    return convertView;
 }

}
user3815165
  • 258
  • 2
  • 16
  • This creates a bug. If I navigate away from the activity and come back to it, the checkboxes that I checked before are still checked, but `listView.getCheckedItemPositions()` says that none are checked. – Yep_It's_Me Sep 20 '14 at 03:23
  • To fix this I would have to iterate through `items` in `onCreate` and call `setOptionCheckbox(false)` on each of them. – Yep_It's_Me Sep 20 '14 at 03:33
  • As far as I know for a boolean always defaults to false. Initialized as false do not need to – user3815165 Sep 22 '14 at 07:23
  • Pretty sure all values need to be initialized. But that's not the problem. It's because the list of `EntryItem` is created when the app first starts, then when I navigate to this Activity and check some boxes the `isOptionCheckbox()` gets set to true. Going back to the first page and coming back to the this Activity again, resets all the CheckBoxes as it's meant to (so `ListView.getCheckedItemPositions()` returns nothing. But some of the CheckBoxes will be checked because the `isOptionValue()` for the `EntryItems` are not reset. – Yep_It's_Me Sep 22 '14 at 22:34
  • Ok. Then I, too, have questions: 1) Why not use a single object instead of SectionItem and EntryItem. 2) Why you can not override the onSaveInstanceState and keep your list, and when you restart this Activity to restore your list – user3815165 Sep 23 '14 at 05:59
  • 1) I the only difference between a SectionItem and an EntryItem is that `SectionItem.isSection()` returns `True` where `EntryItem.isSection()` returns `False`. This lets me choose which layout to inflate as shown in my code with the `if (i.isSection())`, 2) I'm not sure what `onSaveInstanceState` does? I ended up not using `ListView.getCheckedItemPositions()` anyway, because it essentially returns the same thing as the boolean list in @Palash's answer. – Yep_It's_Me Sep 23 '14 at 06:04
  • onSaveInstanceState for object example http://stackoverflow.com/questions/12503836/how-to-save-custom-arraylist-on-android-screen-rotate – user3815165 Sep 23 '14 at 06:32