2

my question is how to access and change the checkBox mode for any item in a listactivity. I have an XML template file with a checkbox and a textview, and these define a row. Here's what I'm trying so far:

@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
    super.onListItemClick(l, v, position, id);
        Toast.makeText(this, "You selected: " + Integer.toString(position), Toast.LENGTH_LONG).show();

        CheckBox checkbox = (CheckBox) findViewById(R.id.checkbox); 
        if (checkbox.isChecked() == false) {
            checkbox.setChecked(true); 
        } else {
            checkbox.setChecked(false); 
        }

}

Obviously though using R.id.checkbox only toggles the first checkbox (actually, it does the first checkbox of whatever part of the list I'm looking at on my screen). I'm not sure what function to use to get the checkbox of any row though. The Toast works fine btw, so at least it registers position properly.

Thanks for any help.

EDIT - I'm now trying to subclass the SimpleCursorAdapter to better control the behaviour I want. Here is that subclass:

public class musicPlaylist extends SimpleCursorAdapter {

private Cursor c;
private Context context;
private ArrayList<String> checkList = new ArrayList<String>();
private final static int SELECTED = 1;
private final static int NOT_SELECTED = 0;

public musicPlaylist(Context context, int layout, Cursor c,
        String[] from, int[] to) {
    super(context, layout, c, from, to);
    this.c = c;
    this.context = context;

}

public View getView(int pos, View inView, ViewGroup parent) {
    View v = inView;
    if (v == null) {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        v = inflater.inflate(R.layout.song_item, null);
    }

    this.c.moveToPosition(pos);     
    int columnIndex = this.c.getColumnIndexOrThrow(MediaStore.Audio.Media.DISPLAY_NAME);
    String song = this.c.getString(columnIndex);

    TextView sTitle = (TextView) v.findViewById(R.id.text1);
    sTitle.setText(song);
    v.setId(NOT_SELECTED);
    v.setTag(song); 
    v.setOnClickListener(new OnClickListener() {



        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub


            if (v.getId() == NOT_SELECTED) {
                v.setId(SELECTED);
                Toast.makeText(context, "Test: " + v.getId(), Toast.LENGTH_SHORT).show();  
                v.setBackgroundColor(Color.parseColor("#FFFFFF"));

            } else {
                v.setId(NOT_SELECTED);
                Toast.makeText(context, "Test: " + v.getId(), Toast.LENGTH_SHORT).show();  
                v.setBackgroundColor(Color.parseColor("#000000"));
            }

        }
    });

    return v; 
}

}

And for reference, here is the XML of the ListActivity I'm making:

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

 <ListView android:id="@android:id/list"
           android:layout_width="fill_parent"
           android:layout_height="fill_parent"
           android:layout_weight="1"
           android:drawSelectorOnTop="false"
           android:fastScrollEnabled="true"
           android:cacheColorHint="#00000000"
           />

 <TextView android:id="@android:id/empty"
           android:layout_width="fill_parent"
           android:layout_height="fill_parent"
           android:text="No data"/>

Current behaviour: the list of songs from the SD card is made into a nice scrollable list. I do get somewhat proper responses from getView()'s onClick: The first time I click an item, it Toasts that its tag is "1" and its background goes white, while the second time I toast the same item, I get "0" and the background goes black, which is as expected.

The problem is if I select item1 (making its background white) and then scroll down, I'll notice that item11, item21, item31, ... , etc ALSO have white backgrounds. But when I click on them, their ID attribute goes to "1", meaning they've technically never been clicked before! So basically when the scroll "refreshes" to the next list of 10, it copies the color scheme of the first 10...?

Hope I explained it clearly.

JDS
  • 16,388
  • 47
  • 161
  • 224

3 Answers3

4

I think this is bit deeper question and not direct answer is needed.

What do you want to achieve? Do you really want to make selected ONLY the checkboxes that you see on screen? Mind that this might be pretty random - list view only holds item views for the checkboxes that are visible on screen and they are reused for other items whenever the item is scrolled outside the screen.

I'd say that almost for sure you need to change state of all the checkboxes in your list (even those not visible) or some subset of them (like section). Which really translates into the proper way it should be done:

  • modify your data model appropriately marking the appropriate flags selected in corresponding data model elements (some boolean values you store per item)
  • call notifyDataSetChanged() on your adapter.

As a result, list view will recreate all the views which are visible on screen. Assuming that your "getView()" in adapter is written correctly, it will read the right model and update checked state on the item appropriately. By notifyDataSetChanged - if you have 10 items visible on screen you will have 10 times getView() called for every item visible.

Jarek Potiuk
  • 19,317
  • 2
  • 60
  • 61
  • I think you're right. What I've done so far is to create a new class that subclasses SimpleCursorAdapter, and I'm overwriting the getView() method. It's going alright, but I tested the onClick for a view by changing the text background color, and I'm getting the "recycle" effect or w/e it's called, where if I select item1 on my screen, it assumes I also selected item11, item21, etc (and changes all the background colors). What do I do to make onClick unique for each view? – JDS Jun 26 '11 at 18:04
  • Ah.. This is another problem. Look at this article - it explains it in detail: http://developer.android.com/resources/articles/listview-backgrounds.html. It is all matter of playing with cacheColorHint (or disabling the optimisation implemented in Android listView) in case you are using custom background. – Jarek Potiuk Jun 26 '11 at 18:10
  • Hmm, I don't think that's my problem because I did the cacheColorHint and I still get the recycling problem of item1's onClick being applied to item11, item21, etc (basically whenever the screen is refreshed to new set of options). Your last sentence of your answer mentions notifyDataSetChanged - should I implement that somewhere? How? – JDS Jun 26 '11 at 18:25
  • Changing the background on all elements might also be caused by not mutating the Drawable. If you get "stateDrawable" and do not mutate it, then it's selected/focused etc. states are shared between different instances of the same drawable (you need to mutate it before using). Calling notifyDataSetChanged() on adapter will make list view to requery adapter for all the visible elements - I do not think this is your problem. You need to post some examples of your code and screenshots of the behaviour observed, because I have a feeling we somehow do not understand each other :). – Jarek Potiuk Jun 26 '11 at 18:32
  • ill edit the OP in a minute and do my best to explain, thanks for sticking around =) – JDS Jun 26 '11 at 18:34
1

I recommend using android:choiceMode="multipleChoice" instead of manually manipulating your rows this way. The row widget will need to implement the Checkable interface, which can either be done by using CheckedTextView as the row itself, or creating a subclass of your desired container and implementing Checkable on it.

Community
  • 1
  • 1
CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
0

You use "global" view instead your row view. Try like that:

 @Override
 protected void onListItemClick(ListView l, View v, int position, long id) {
   super.onListItemClick(l, v, position, id);

    CheckBox checkbox = (CheckBox) v.findViewById(R.id.checkbox); 
    if (checkbox.isChecked() == false) {
        checkbox.setChecked(true); 
    } else {
        checkbox.setChecked(false); 
    }
 }
mtfk
  • 890
  • 6
  • 12