Почему ListView.getCheckedItemPositions() не возвращает правильные значения?
В приложении есть ListView с включенным множественным выбором, в пользовательском интерфейсе он работает так, как ожидалось. Но когда я прочитал значения, используя этот код:
Log.i(TAG,"Entered SearchActivity.saveCategoryChoice()");
SparseBooleanArray checkedPositions = categorySelector.getCheckedItemPositions();
Log.i(TAG,"checkedPositions: " + checkedPositions.size());
if (checkedPositions != null) {
int count = categoriesAdapter.getCount();
for ( int i=0;i<count;i++) {
Log.i(TAG,"Selected items: " + checkedPositions.get(i));
}
}
Я получаю этот вывод, независимо от того, в каком состоянии находится каждый флажок:
Entered SearchActivity.saveCategoryChoice()
checkedPositions: 0
Selected items: false
Selected items: false
Selected items: false
Selected items: false
Selected items: false
Selected items: false
Selected items: false
Selected items: false
Selected items: false
Selected items: false
Selected items: false
Selected items: false
Selected items: false
Selected items: false
Selected items: false
Selected items: false
Selected items: false
SparseBooleanArray, кажется, возвращает false для любого несуществующего элемента, поэтому источником проблем является то, что getCheckedItemPositions ( ) возвращает пустой массив. Метод ведет себя так, как будто в ListView нет элементов, но есть.
В документах я вижу, что никакие значения не возвращаются, когда ListView не настроен как multi-select, но он использует это утверждение:
categorySelector.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
В моем сценарии адаптер, который я использую, является подклассом ArrayAdapter, и (без каких-либо убедительных доказательств) я подозреваю, что это может быть причиной, хотя я не понимаю, почему это не должно работать.
Ответы
Ответ 1
Я все еще не знаю, почему, но в моем сценарии getCheckedItemPositions()
возвращает значения false для всех элементов. Я не вижу способа использовать методы в ListView для получения логических значений. Объект SparseBooleanArray, похоже, не содержит реальных данных. Я подозреваю, что это может быть из-за некоторой причуды моей реализации, возможно, что я подклассифицировал ArrayAdapter. Это разочаровывает, такие проблемы, как это, - это реальное время-утечка.
Во всяком случае, решение, которое я использовал, состоит в том, чтобы прикрепить обработчик к каждому флажку индивидуально, поскольку создаются строки ListView. Поэтому из ListView.getView()
я вызываю этот метод:
private void addClickHandlerToCheckBox(CheckBox checkbox) {
checkbox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton arg0, boolean arg1) {
CheckBox checkbox = (CheckBox)arg0;
boolean isChecked = checkbox.isChecked();
// Store the boolean value somewhere durable
}
});
}
Ответ 2
kcoppock прав, вам нужно использовать valueAt(), рабочий код должен быть
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");
}
}
}
Ответ 3
Я помню, что у меня была проблема с этим некоторое время назад. Здесь - мой предыдущий вопрос, который напрямую не связан с вашей проблемой, но содержит некоторый код, который может помочь. Вы можете попробовать использовать checkedPositions.valueAt(int index)
, а не checkedPositions.get(int index)
. Я думаю, это может быть то, что вы действительно ищете.
Ответ 4
Это происходит, если вы не выбрали правильный ресурс для показа ваших разных предметов. Он отлично работает, если вы выберете встроенный ресурс android.R.layout.simple_list_item_multiple_choice. Метод getCheckedItemPositions
каким-то образом связан со встроенным ресурсом.
Ответ 5
Ни одно из вышеперечисленных решений не сработало для меня, вместо этого я получаю каждый дочерний элемент (checkedTextView) из ListView и вижу, проверен ли он или нет:
ListView myListView = myViewActivity.getListView();
ArrayList<String> selectedChildren2 = new ArrayList<String>();
for(int i = 0;i<myListView.getChildCount();i++)
{
CheckedTextView c = (CheckedTextView) myListView.getChildAt(i);
if(c.isChecked())
{
String child = c.getText().toString();
selectedChildren.add(child);
}
}
Ответ 6
Нет необходимости обрабатывать проверку/снятие отметки элементов в ListView. Он уже делает это самостоятельно.
Что не задокументировано, так это то, что ListView будет делать это только в том случае, если:
- a
ListAdapter
установлен и
- Режим выбора: CHOICE_MODE_MULTIPLE и
- Иды, используемые
ListAdapter
, стабильны.
Третий момент - это то, что меня немного смутило.
Я не уверен, что означает "стабильный" (я думаю, что идентификаторы никогда не изменяются, пока отображается список).
Что касается ListView, это означает, что метод hasStableIds()
в ListAdapter
возвращает true.
Я создал простой подкласс ArrayAdapter
следующим образом:
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;
}
}
(У вас уже есть свой подкласс, поэтому просто добавьте переопределение hasStableIds
)
Конечно, нужно добавить конструктор, который использовался с помощью ArrayAdapter
.
Как только вы используете этот класс в качестве ListAdapter
, getCheckedItemPositions()
ведет себя как ожидалось.
Последнее примечание: setChoiceMode
необходимо вызвать ПОСЛЕ настройки адаптера списка.
Ответ 7
Это старый поток, но так как это в основном появилось в текущем поиске Google, здесь можно быстро понять, что делает listView.getCheckedItemPositions()
:
Если элемент списка не был "переключен" вообще в вашем ListView, он не будет добавлен в SparseBooleanArray, который возвращается listView.getCheckedItemPositions()
Но тогда вы действительно не хотите, чтобы ваши пользователи щелкали каждый элемент списка, чтобы "правильно" добавить его в возвращаемый SparseBooleanArray?
Следовательно, вам нужно объединить использование valueAt() И keyAt() для SparseBooleanArray для этого.
SparseBooleanArray checkedArray = listView.getCheckedItemPositions();
ArrayList<DataEntry> entries = baseAdapter.getBackingArray(); //point this to the array of your custom adapter
if (checkedArray != null)
{
for(int i = 0; i < checkedArray.size(); i++)
{
if(checkedArray.valueAt(i)) //valueAt() gets the boolean
entries.yourMethodAtIndex(checkedArray.keyAt(i)); //keyAt() gets the key
}
}
Ответ 8
Нет ли принципиальной разницы между 'selected'
и 'checked'
?
Подождите, вы хотите setItemChecked()
от OnItemSelectedListener
...
Ответ 9
Эта настройка исправила это для меня.
Измените метод getView так, чтобы "int position" было "окончательной int position".
Затем сделайте это с помощью своего флажка:
((CheckBox) convertView).setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
list.setItemChecked(position, ((CheckBox) buttonView).isChecked());
}
});
Позвольте мне уточнить.
list - это ссылка на список, и в моем случае адаптер является внутренним классом в диалоговом окне, содержащем список.
Ответ 10
Я нашел эту тему, имея ту же проблему, но я думаю, что придумал обходной путь, который работал у меня по неизвестным причинам. Всякий раз, когда я пытался получить значение, я ничего не получал, но если бы я пропустил список, установив все в false, он начал работать так же, как и предполагалось.
Это была функция, которую я реализовал, когда пользователь мог либо "Выбрать все", либо "Отменить выбор всех". Я запускаю этот метод в своем onCreate.
private void selectNone() {
ListView lv = getListView();
for (int i = 0; i < lv.getCount(); i++) {
lv.setItemChecked(i, false);
}
}
Теперь все мои значения верны. Для получения значений, в моем случае, просто строк.
private void importSelected() {
ListView lv = getListView();
SparseBooleanArray selectedItems = lv.getCheckedItemPositions();
for (int i = 0; i < selectedItems.size(); i++) {
if (selectedItems.get(i)) {
String item = lv.getAdapter().getItem(selectedItems.keyAt(i)).toString();
}
}
selectNone(); //Reset
}
Я надеюсь, что это поможет кому-то.
Ответ 11
Я тоже использовал решение gyller, которое предполагает "инициирование" списка
ListView lv = getListView();
for (int i = 0; i < lv.getCount(); i++) {
lv.setItemChecked(i, false);
}
перед вызовом getCheckItemPositions(), и он прекратил выдавать ошибочные результаты!
Ответ 12
в то время как я не верю, что я пробовал все описанные здесь варианты, вот тот, который работал у меня:)
@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());
}
});
...
}
И позже
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);
}
Я получаю доступ к позиции + 1 из-за заголовка, который имеет мой список.
НТН
Ответ 13
ArrayList<Integer> ar_CheckedList = new ArrayList<Integer>();
для каждого холера флажка, который я использую, чтобы сохранить его в массиве
holder.chk_Allow.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(isChecked)
ar_CheckedList.add(position);
}
});
при нажатии кнопки
for (int i = 0; i < ar_CheckedList.size(); i++)
{
HashMap<String, String> temp=(HashMap<String, String>) contactList.get(ar_CheckedList.get(i));
str_Phone_No=temp.get(TAG_CONTACT_MOBILE);
send(str_Phone_No);
}
Ответ 14
Выбран ArrayListChildren = новый ArrayList();
for(int i = 0;i<list.getChildCount();i++)
{
CheckBox c = (CheckBox) list.getChildAt(i);
if(c.isChecked())
{
String child = c.getText().toString();
selectedChildren.add(child);
}
}
Log.i(TAG, "onClick: " + selectedChildren.size());
Ответ 15
просто перейдите в xml, где определено ваше listview и задано свойство
**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>
а затем в вашем javafile как: -
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();
}
}