【问题标题】:Why is ListView.getCheckedItemPositions() not returning correct values?为什么 ListView.getCheckedItemPositions() 没有返回正确的值?
【发布时间】:2010-10-22 12:52:08
【问题描述】:

该应用有一个启用了多选功能的 ListView,在 UI 中它按预期工作。但是当我使用此代码读出值时:

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 未设置为多选时不会返回任何值,但使用以下语句:

categorySelector.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);

在我的场景中,我使用的适配器是 ArrayAdapter 的子类,并且(没有任何确凿证据)我怀疑这可能是原因,但我不明白为什么它不应该工作。

【问题讨论】:

  • 很遗憾我没有早点看到这个问题(几年),但是您是如何检查 ListView 的项目的?上面的代码和结果都没有明显的错误。如果您使用 categorySelector.setItemChecked(int position, boolean value) 设置选中状态,它应该可以工作。

标签: android listview


【解决方案1】:

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");
        }
    }
}

【讨论】:

    【解决方案2】:

    我记得前段时间我自己也遇到过这个问题。 Here 是我之前的问题,与您的问题没有直接关系,但包含一些可能有帮助的代码。您可能想尝试的是使用checkedPositions.valueAt(int index) 而不是checkedPositions.get(int index)。我认为这可能是您真正想要的。

    【讨论】:

    • 我刚刚尝试使用 valueAt() 对我来说,无论出于何种原因,无论复选框设置为什么,它都会为所有这些返回 false。
    【解决方案3】:

    我仍然不知道为什么,但在我的场景中,getCheckedItemPositions() 为所有项目返回错误值。我看不到使用 ListView 上的方法获取布尔值的方法。 SparseBooleanArray 对象中似乎没有实际数据。我怀疑这可能是因为我的实现有些怪癖,也许是我将 ArrayAdapter 子类化了。令人沮丧,像这样的问题是实时消耗。

    无论如何,我使用的解决方案是在创建 ListView 行时分别将处理程序附加到每个 Checkbox。所以从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
          }
      });
    }
    

    【讨论】:

    • 您能解释一下如何为您的复选框列表中的每个复选框添加 addClickHandlerToCheckBox 吗?谢谢。
    • 问题可能是适配器的getView()返回的视图没有实现Checkable接口。
    • 我遇到了类似的问题,但这是由于确认对话框清除了选择。您需要先克隆SparseBooleanArray,然后再清除选中的状态(自动)。
    【解决方案4】:

    上述解决方案均不适合我,相反,我从 ListView 中获取每个子项(checkedTextView)并查看它是否被选中:

                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);
                    }
            }
    

    【讨论】:

    • 如果你的元素比可见的多,这将不起作用,因为它只会遍历可见的行。
    【解决方案5】:

    如果您没有选择正确的资源来展示您的不同项目,就会发生这种情况。如果您选择内置资源 android.R.layout.simple_list_item_multiple_choice,它工作正常。方法getCheckedItemPositions 以某种方式与内置资源耦合。

    【讨论】:

      【解决方案6】:

      无需处理 ListView 中项目的选中/取消选中。它已经自行完成了。

      似乎没有记录的是 ListView 只会在以下情况下执行此操作:

      1. ListAdapter 已设置并
      2. 选择模式是 CHOICE_MODE_MULTIPLE 和
      3. ListAdapter 使用的 id 是稳定的

      第三点让我疯狂了一阵子。

      我不确定“稳定”是什么意思(我猜想显示列表时 id 永远不会改变)。 就ListView而言,表示ListAdapter中的方法hasStableIds()返回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的构造函数。

      一旦您将此类用作您的ListAdaptergetCheckedItemPositions() 就会按预期运行。

      最后一点:setChoiceMode 必须在设置列表适配器之后调用。

      【讨论】:

        【解决方案7】:

        这是一个旧线程,但由于这基本上是当前 Google 搜索中最先出现的,因此这是了解 listView.getCheckedItemPositions() 作用的快速方法:

        除非列表项在您的 ListView 中根本没有“切换”,否则它不会被添加到由 listView.getCheckedItemPositions() 返回的 SparseBooleanArray 中

        但是,您真的不希望您的用户单击每个列表项以“正确地”将其添加到返回的 SparseBooleanArray 中吗?

        因此,您需要为此结合使用 SparseBooleanArray 的 valueAt()keyAt()

            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
                }
            }
        

        【讨论】:

        • 作者的问题是checkedPositions.size()(你的情况下是checkedArray.size())总是返回0。
        • 最大的问题是,为什么检查不SparseBooleanArray checkedArray = listView.getCheckedItemPositions() 清除当前未检查但过去检查过的项目!
        【解决方案8】:

        这个调整为我解决了这个问题。 更改 getView 方法,使“int position”为“final int position”。 然后使用您的复选框执行此操作:

        ((CheckBox) convertView).setOnCheckedChangeListener(new OnCheckedChangeListener() { public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { list.setItemChecked(position, ((CheckBox) buttonView).isChecked()); } });

        让我详细说明。 list 是对 listview 的引用,在我的情况下,适配器是保存列表的对话框中的内部类。

        【讨论】:

          【解决方案9】:

          我通过遇到同样的问题找到了这个帖子,但我想我想出了一个解决方法,它对我有用,原因不明。每当我尝试获取一个值时,我什么也没得到,但如果我循环遍历列表,将 all 设置为 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
          }
          

          我希望这对某人有所帮助。

          【讨论】:

            【解决方案10】:

            我也使用了 gyller 建议的涉及“启动”列表视图的解决方案

            ListView lv = getListView(); for (int i = 0; i < lv.getCount(); i++) { lv.setItemChecked(i, false); }

            在调用 getCheckItemPositions() 之前,它停止产生错误结果!

            【讨论】:

              【解决方案11】:

              虽然我不相信我已经尝试过这里描述的所有变体,但这是对我有用的一个:)

                      @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。

              HTH

              【讨论】:

                【解决方案12】:
                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);
                 }
                

                【讨论】:

                  【解决方案13】:

                  'selected''checked' 之间不是有相当根本的区别吗?

                  怀疑你想从OnItemSelectedListenersetItemChecked()...

                  【讨论】:

                  • 你说得对,我在我的问题中检查并选择了一点,但是...... getCheckedItemPositions() 的文档确实声明选择模式必须是多重的,否则它返回空值。我也尝试过 getCheckItemIds() 并且它也返回一个空集合。我怀疑我最终会使用监听器,但我很好奇为什么这些方法不能直观地工作。
                  • 是的,为什么他们让选择模式完全相关,这有点令人困惑。刚才浏览了 ListView.java,在我看来,除非你自己调用 setItemChecked(),否则我认为你不会得到自动检查状态切换,你(相当合理地)假设应该在选择项目时发生。我可以看到的唯一自动检查状态切换发生在添加列表项时,并且只有在您的 View 派生类实现 Checkable 时才会发生。如果默认选择处理做同样的事情会很好......
                  • 我认为这是有道理的,但仍然很混乱。如果我有一个带有复选框的多选 ListView 并且有一个名为 getCheckedItemIds() 的方法,那么假设它将提供状态似乎是合理的。但是我已经使用 OnCheckedChangeListener 上的处理程序重新实现了它,并且工作正常,实际上使用 getTag() 这是一种很好且干净的方法。感谢您分享您的想法。
                  【解决方案14】:

                  只需转到定义列表视图的 xml 并设置属性

                  **android:choiceMode="multipleChoice"**
                  

                  我的 xml 文件

                  <?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();           
                  
                     }
                  }
                  

                  【讨论】:

                    【解决方案15】:
                    ArrayList<String> selectedChildren = new ArrayList<String>();
                    
                    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());
                    

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 2014-04-08
                      • 1970-01-01
                      • 1970-01-01
                      • 2017-09-13
                      • 1970-01-01
                      • 2015-03-21
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多