The app has a ListView with multiple-selection enabled, in the UI it works as expected. But when I read the values out using this code:
Log.
There is no need to handle the checking/unchecking of items within the ListView. It already does it on its own.
What does not seem documented is that the ListView will only do this if:
ListAdapter
is set and ListAdapter
are stable.The third point was what drove me crazy for a while.
I am not sure what 'stable' means (I guess that the ids don't ever change while the list is displayed).
As far as the ListView is concerned, it means that the method hasStableIds()
in ListAdapter
returns true.
I created a simple subclass of ArrayAdapter
like this:
public class StableArrayAdapter<T> extends ArrayAdapter<T> {
public StableArrayAdapter(Context context, int textViewResourceId, List<T> objects) {
super(context, textViewResourceId, objects);
}
@Override
public boolean hasStableIds() {
return true;
}
}
(You already have your subclass, so just add the hasStableIds
override)
Of course, one needs to add the constructor that one was using with ArrayAdapter
.
Once you use this class as your ListAdapter
, getCheckedItemPositions()
behaves as expected.
One last note: setChoiceMode
must be called AFTER setting the list adapter.
while I do not believe I have tried every variation described here, here is the one that has worked for me :)
@Override
public View getView(final int position, View convertView, ViewGroup parent)
{
CheckedTextView retView = (CheckedTextView) convertView;
...
retView.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
CheckedTextView chkVw = (CheckedTextView) v;
// chkVw.toggle();
// chkVw.setChecked(!chkVw.isChecked());
mLstVwWordingSets.setItemChecked(position + 1, !chkVw.isChecked());
}
});
...
}
And later
SparseBooleanArray checkedItemsArray = mLstVwWordingSets.getCheckedItemPositions();
for (int i = 1; i < mLstVwWordingSets.getCount(); i++) //skip the header view
{
if (checkedItemsArray.get(i, false))
Log.d(_TAG, "checked item: " + i);
}
I am accessing position + 1 due to a header view that my list has in place.
HTH
I too used the solution gyller suggests that involves "initiating" the listview
ListView lv = getListView();
for (int i = 0; i < lv.getCount(); i++) {
lv.setItemChecked(i, false);
}
before calling getCheckItemPositions(), and it stopped producing erroneous results!
I remember having an issue with this myself a while back. Here is my previous question, which isn't directly related to your issue, but contains some code that may help. What you might want to try is using checkedPositions.valueAt(int index)
rather than checkedPositions.get(int index)
. I think that may be what you're actually looking for.
kcoppock is right, you need to use valueAt(), the working code should be
SparseBooleanArray checkedItems = categorySelector.getCheckedItemPositions();
if (checkedItems != null) {
for (int i=0; i<checkedItems.size(); i++) {
if (checkedItems.valueAt(i)) {
String item = categorySelector.getAdapter().getItem(
checkedItems.keyAt(i)).toString();
Log.i(TAG,item + " was selected");
}
}
}
simply go to the xml where your listview is defined and set property
**android:choiceMode="multipleChoice"**
my xmlfile
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.ali.myapplication.MainActivity">
<ListView
android:id="@+id/lvCustomList"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:choiceMode="multipleChoice"
/>
</android.support.constraint.ConstraintLayout>
and then in your javafile as:-
SparseBooleanArray checked=lvDetail.getCheckedItemPositions();
for (int i = 0; i < lvDetail.getAdapter().getCount(); i++) {
if (checked.get(i)) {
Toast.makeText(getApplicationContext(),checked.get(i),Toast.LENGTH_SHORT).show();
}
}