How to get all checked items from a ListView?
Asked Answered
E

10

45

I've a couple of question I haven't been able to figure out.

I'm trying to get all the checked elements from a ListView but:

  1. If I check and then uncheck an element, it's returned as "checked" by the getCheckedItemPositions() function

  2. I don't know how can I iterate through this:

    SparseBooleanArray checked = list.getCheckedItemPositions();
    
Endmost answered 28/1, 2011 at 18:49 Comment(2)
give a look, maybe helps : #4019808 #3997438 #4011123Bernadine
starting API level 11 you can use list.getCheckedItemCount();Capeskin
E
11

I solved my case with this:

public class MyAdapter extends BaseAdapter{
    public HashMap<String,String> checked = new HashMap<String,String>();
....
    public void setCheckedItem(int item) {


        if (checked.containsKey(String.valueOf(item))){
            checked.remove(String.valueOf(item));
        }

        else {
            checked.put(String.valueOf(item), String.valueOf(item));
        }
    }
        public HashMap<String, String> getCheckedItems(){
        return checked;
    }
}

To set an element is checked:

public class FileBrowser extends Activity implements OnClickListener{        
private ListView list;

    ...
    list.setOnItemClickListener(new OnItemClickListener(){

        @Override
        public void onItemClick(AdapterView<?> parent, View view, int item,
                long id) {

                        BrowserAdapter bla = (BrowserAdapter) parent.getAdapter();
                        bla.setCheckedItem(item);
                    }
    });

Then to get the checked items from outside the class..

MyAdapter bAdapter;    
Iterator<String> it = bAdapter.getCheckedItems().values().iterator();
                    for (int i=0;i<bAdapter.getCheckedItems().size();i++){
                        //Do whatever
                        bAdapter.getItem(Integer.parseInt(it.next());
                    }

Hope it can help someone.

Endmost answered 16/7, 2011 at 7:48 Comment(2)
This did exactly what I needed it to. If i wanted to limit selection to 1 item, how should I protect against that?Housemother
I think it will not work unless you do it.next() - 1 when iterating in the for loop through the entire collection. By doing it.next(), in case checkeditems list contains only 1 item it will throw NoSuchElementException and in case it has more than 1 item, first item is skipped.Lungworm
C
81

The other answers using SparseBooleanArray are nearly correct, but they are missing one important thing: SparseBooleanArray.size() will sometimes only return the count of true values. A correct implementation that iterates over all the items of the list is:

SparseBooleanArray checked = list.getCheckedItemPositions();

for (int i = 0; i < list.getAdapter().getCount(); i++) {
    if (checked.get(i)) {
        // Do something
    }
}
Consume answered 16/10, 2013 at 21:52 Comment(13)
Ironically, this is the only correct (and sane) answer and didn't have a single vote.Corrugation
Why do you need to iterate over the whole list? Isn't the getCheckedItemPositions() method designed to avoid this? Why just not iterate over already selected for you by getCheckedItemPositions() method items? Foo objectAtCheckedRow = null; for (int i = 0; i < positions.size(); i++) { /*positions.size() == 2*/ objectAtCheckedRow = adapter.getItem(positions.keyAt(i)); /*Do something significant with object here*/ }Winwaloe
@Winwaloe It's possible to do it that way if you only need the checked items, but you can't assume that all the items returned by getCheckedItemPositions() are checked. Try checking an item and then unchecking it; the SparseBooleanArray will now contain an entry for that position with a value of false.Consume
@Jarett First, the author asked how to get all checked items. (And it is reasonable). Next, why would you check an item then get SparseBooleanArray object then uncheck the item (and possibly rotate the device) and then use the stale SparseBooleanArray object? Do all check and uncheck actions first and then get fresh SparseBooleanArray object and use it. Anyway if you apply the code in your answer to the described scenario it will yield same result as my code except that my code will iterate over only a few checked items whereas your code will iterate over the whole list.Winwaloe
The code I showed in my comment I got from Kevin Worth's answer to this question: #4591356Winwaloe
@Winwaloe I'm not talking about using a stale SparseBooleanArray; I'm saying that if the user checks an item and then unchecks it before you call getCheckedItemPositions() it can still appear in the retrieved array with a value of false. If the boolean states were always true then the call could simply return an array of ints for checked positions.Consume
The is an old question, but I just stumbled upon it and it seems to me that there is a bug in the answer with the most votes. The SpareBooleanArray get(int key) method takes a key, but here it is being passed an index. If the keys are [10, 99, 100] and the values are [true, true, true], you will be asking for [0, 1, 2] and receiving [false, false, false] The correct answer is: for (int i = 0; i < list.getAdapter().getCount(); i++) { if (checked.ValueAt(i)) { // Do something } }Cubitiere
@Jarett I am sorry, I didn't fully understand your previous comment: "... you can't assume that all the items returned by getCheckedItemPositions() are checked. Try checking an item and then unchecking it; the SparseBooleanArray will now contain an entry for that position with a value of false".Winwaloe
@Jarett So yes, my code is wrong and it wouldn't yield same result as yours. I must check each item returned from SparseBooleanArray: for (int i = 0; i < positions.size(); i++) { if (positions.valueAt(i)) { Foo objectAtCheckedRow = adapter.getItem(positions.keyAt(i)); /*Do something significant with object here*/ } } But still I think there is no need to iterate over whole list.Winwaloe
@Cubitiere The code will function properly as written since it iterates over every possible position. No key will be more than list.getAdapter().getCount() so all possible keys will be checked. To Sergey's point, if you don't iterate over every item then you would need to use valueAt(i).Consume
@Winwaloe I agree, iterating over the list is not always necessary. However, sometimes you will want to process both checked and unchecked items. This method is also guaranteed to preserve the order of the checked items, which can be important for some use cases. Also, the number of items in a checkable list is limited by practicality: nobody is going to scroll through a list of thousands of items and check some off, so the performance gain is very small. I think the upsides outweigh the negligible performance decrease of iterating through the entire list.Consume
@JarettMillard , you are correct. I went to look at the ListView code and it does indeed the full list of values, not just the checked ones.Cubitiere
Actually I ran into a problem and only this answer is correct. I have a list with up to 10 check-boxes. If the last two are checked, the 'getCheckedItemPositions()' will return the entire list, but if I ask for the size it will return 2, since only 2 items are checked, and only iterate through the first two items, never reaching the checked ones. Only by getting the size from the ListAdapter as demonstrated by Jarett can I iterate through the entire list. This should be marked as the correct answer.Supremacist
E
11

I solved my case with this:

public class MyAdapter extends BaseAdapter{
    public HashMap<String,String> checked = new HashMap<String,String>();
....
    public void setCheckedItem(int item) {


        if (checked.containsKey(String.valueOf(item))){
            checked.remove(String.valueOf(item));
        }

        else {
            checked.put(String.valueOf(item), String.valueOf(item));
        }
    }
        public HashMap<String, String> getCheckedItems(){
        return checked;
    }
}

To set an element is checked:

public class FileBrowser extends Activity implements OnClickListener{        
private ListView list;

    ...
    list.setOnItemClickListener(new OnItemClickListener(){

        @Override
        public void onItemClick(AdapterView<?> parent, View view, int item,
                long id) {

                        BrowserAdapter bla = (BrowserAdapter) parent.getAdapter();
                        bla.setCheckedItem(item);
                    }
    });

Then to get the checked items from outside the class..

MyAdapter bAdapter;    
Iterator<String> it = bAdapter.getCheckedItems().values().iterator();
                    for (int i=0;i<bAdapter.getCheckedItems().size();i++){
                        //Do whatever
                        bAdapter.getItem(Integer.parseInt(it.next());
                    }

Hope it can help someone.

Endmost answered 16/7, 2011 at 7:48 Comment(2)
This did exactly what I needed it to. If i wanted to limit selection to 1 item, how should I protect against that?Housemother
I think it will not work unless you do it.next() - 1 when iterating in the for loop through the entire collection. By doing it.next(), in case checkeditems list contains only 1 item it will throw NoSuchElementException and in case it has more than 1 item, first item is skipped.Lungworm
C
8

Jarett's answer is great, but this should be a bit faster, since it's only checking the elements in the sparse array that are present in the underlying array (only those can be true):

SparseBooleanArray checkedItemPositions = listView.getCheckedItemPositions();
    final int checkedItemCount = checkedItemPositions.size();
    for (int i = 0; i < checkedItemCount; i++) {
        int key = checkedItemPositions.keyAt(i);
        if (checkedItemPositions.get(key)) {
            doSomething(key);
        } else {
            // item was in the sparse array, but not checked.
        }
    }

Pro tip: look at the source of SparseBooleanArray, it's a pretty simple class:

http://grepcode.com/file_/repository.grepcode.com/java/ext/com.google.android/android/4.0.3_r1/android/util/SparseBooleanArray.java/?v=source

Chicoine answered 25/3, 2014 at 1:30 Comment(0)
C
6

You can iterate through the SparseBooleanArray using a for loop and the SparseBooleanArray's size() and get(index) methods.

EDIT 3/2/2014: People have pointed out that SparseBooleanArray's size() method returns the number of checked values, rather than the true length of the array, so I have mended my answer to account for that. Essentially it is the same, except that rather than iterating for the length of the array, we iterate until we have found all checked items. Since we only care about the number of checked items, it's irrelevant that with this method, we may not actually get to the end of the array (we won't see anything past the last checked item).

SparseBooleanArray checked = list.getCheckedItemPositions();

int numChecked = checked.size();

for (int i = 0; numChecked > 0; i++){
    if (checked.get(i)){
        //the item at index i is checked, do something
        numChecked--; //We found a checked item, so decrement the number of checked items remaining
    }
    else
        //the item is not checked, do something else
}
Cult answered 6/6, 2012 at 4:3 Comment(3)
SparseBooleanArray.size() will only return the count of true values not of all values.Coprology
SparseBooleanArray.size() ... "Returns the number of key-value mappings that this SparseBooleanArray currently stores." from developer.android.com/reference/android/util/… . that is not necessarily only true values.Chicoine
Yes, but the size of the SparseBooleanArray returned from ListView.getCheckedItemPositions() will only be equal to the number of checked items. Unchecked items will not show up in the SparseBooleanArray.Consume
L
6

My brain didn't like looping through the SparseBooleanArray and I didn't need a custom adapter, so the following was a little more intuitive for me:

  1. Don't forget to use CHOICE_MODE_MULTIPLE in onCreate():

    getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);

  2. Use the following method to get an ArrayList of them:

    // This example is to get an ArrayList of names (Strings)
    protected ArrayList<String> getNames() {
        ArrayList<String> names = new ArrayList<String>();
    
        for (int i = 0; i < getListView().getCount(); i++) {
            if (getListView().isItemChecked(i)) {
                // Do whatever you need to in here to get data from
                // the item at index i in the ListView
                names.add(mUsers.get(i).getName());
            }
        }
    
        return names;
    } 
    
Lamppost answered 5/1, 2014 at 7:7 Comment(1)
This is perfect. No reason why anyone should plunge into the hell hole that is SparseBooleanArray.Lemuroid
B
2
 final long[] checkedIds = lv.getCheckItemIds();
            for (int i = 0; i < checkedIds.length; i++) {
                Log.d("checkedIds", "id checked: " + checkedIds[i]);
            }
Brantbrantford answered 22/5, 2012 at 19:5 Comment(1)
NB: This method is deprecated and you should use getCheckedItemIds() instead (available from API 8 / Android 2.2).Indiscrimination
R
1

Just saw the question and I was facing the same problem.

There is a simpler solution using SparseBooleanArray to exactly count how many items are checked. This is my onClick procedure:

@Override
public void onClick(View v) {
switch(v.getId()) {
  case R.id.button:
    SparseBooleanArray a = listView.getCheckedItemPositions();
    if(checked(vArray)>0) {
            String vCheckedList = "";
            for (int i = 0; i < nLength; i++) {
                if (a.valueAt(i) && i < nLength-1 && a.size()>1)
                    vCheckedList += listView.getAdapter().getItem(vArray.keyAt(i))+"\n";
                else if (a.valueAt(i))
                    vCheckedList += listView.getAdapter().getItem(vArray.keyAt(i));
            }
            Toast.makeText(getApplicationContext(), vCheckedList+ " is checked", Toast.LENGTH_SHORT).show(); 
            a.clear();
        } else
            Toast.makeText(getApplicationContext(), "No Item is Selected", Toast.LENGTH_SHORT).show();
        break;
    default:
        break;
        }
}

The checked method:

private int checked(SparseBooleanArray vArray) {
    int vCounter = 0;
    for(int i=0;i<vArray.size(); i++)
        if(vArray.valueAt(i))
            vCounter++;
    return vCounter;

}

It will solve both problem of the checked items.

Revelatory answered 18/1, 2012 at 6:52 Comment(0)
E
1

I had the same problem and here is my solution with SparseBooleanArray :

SparseBooleanArray checkedPositions = lv.getCheckedItemPositions ();
int size = checkedPositions.size ();
for (int i=0 ; i<size ; i++) {
     // We get the key stored at the index 'i'
     int key = checkedPositions.keyAt (i);
     // We get the boolean value with the key
     Log.i (Tag, "checkedPositions(" + key + ")=" + checkedPositions.get (key));
}
Edvard answered 3/4, 2013 at 0:40 Comment(0)
C
0

In it's entirety this is the solution I used.

Where pos is the Recyclerview item position

SparseBooleanArray selected = new SparseBooleanArray();

//OnClick listener here or whatever you use to check or uncheck an item
if (selected.get(pos, false)) {
        selected.delete(pos);
    } else {
        selected.put(pos, true);
    }

selected.size() will reveal how many items are selected

selected.get(pos) will return true if it's selected

The following as has been stated, iterates over all those items which were selected:

for (int i = 0; i < list.getAdapter().getCount(); i++) {
    if (selected.get(pos)) {
        // Do stuff
    }
}
Crystallo answered 13/1, 2016 at 5:22 Comment(0)
C
0

Here is how I get checked items from recyclerview adapter

fun getAllCheckedItems(outList){
         for (position in spareBooleanArray.keyIterator()) {
            outList.add(mList.get(position)
        }
}
Contrariety answered 27/4, 2021 at 10:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.