【问题标题】:ListView selection remains persistent after exiting choice mode退出选择模式后,ListView 选择保持不变
【发布时间】:2012-03-17 22:04:44
【问题描述】:

我有一个 ListView 子类,我允许在上下文操作栏 (CAB) 处于活动状态时进行选择。 CAB 设置为对onItemLongClick 事件的回调:

public boolean onCreateActionMode(ActionMode mode, Menu menu) {
    // Inflate a menu resource providing context menu items
    MenuInflater inflater = mode.getMenuInflater();
    inflater.inflate(context_menu, menu);
    getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
    return true;
}

这很好,ListView 按预期工作,当前选定的项目在触摸时保持突出显示。

当我关闭 CAB 时,我希望 ListView 恢复正常(即触摸模式)。问题是最后选择的项目无限期地保持突出显示,无论我尝试什么方法清除它:

public void onDestroyActionMode(ActionMode mode) {
    //Unselect any rows
    ListView lv = getListView();
    lv.clearChoices(); // Has no effect
    lv.setChoiceMode(ListView.CHOICE_MODE_NONE); // Has no effect on the highlighted item 
    lv.setFocusable(false); // Has no effect
    lv.setSelection(0); // Has no effect
    mActionMode = null;
}

有什么建议吗?

【问题讨论】:

    标签: java android android-listview


    【解决方案1】:

    问题的主要原因是一旦ListView选择模式切换到CHOICE_MODE_NONE,框架优化了clear操作,因为它不再支持“选择”。我通过手动清除选择状态然后以延迟方式设置模式来稍微改进上述解决方法,因此框架将在将模式转换为CHOICE_MODE_NONE之前轮到清除状态。

    final ListView lv = getListView();
    lv.clearChoices();
    for (int i = 0; i < lv.getCount(); i++)
        lv.setItemChecked(i, false);
    lv.post(new Runnable() {
        @Override
        public void run() {
            lv.setChoiceMode(ListView.CHOICE_MODE_NONE);
        }
    });
    

    【讨论】:

    • 这实际上是做到这一点的最好的也是最简单的方法。
    • 嗯...我在这样做时得到StackOverflowErrors。再次调用setItemChecked() 会触发onDestroyActionMode()
    • getChildCount()getCount() 不同。 getChildCount 包括页眉和页脚,并且仅包括在列表中可见的视图(从 0 开始,无论您在列表中滚动到哪个位置)。 setItemChecked() 采用 position 参数,该参数与子索引所代表的位置不同。
    • 实际上,我没有看到这里需要lv.clearChoices(),没有它的代码也对我有用。
    • 根据 Joe 的建议修复了使用 getCount() 的代码。
    【解决方案2】:

    我遇到了同样的问题,因为请求布局并不能解决我的问题,所以我实施了一个对我有用的小技巧。也许这是同一个问题,因为我在CHOICE_MODE_SINGLECHOICE_MODE_NONE 之间切换。

    当动作模式结束时,我将调用此代码 sn-p。 clearChoices 确保不再检查所有项目(内部)。视图的迭代确保所有当前可见的视图都被重置并且不再检查。

    mListView.clearChoices();
    
    for (int i = 0; i < mListView.getChildCount(); i++) {
        ((Checkable) mListView.getChildAt(i)).setChecked(false);
    }
    
    mListView.setChoiceMode(ListView.CHOICE_MODE_NONE);
    

    【讨论】:

    • 我认为这是 11 岁以上没有 AbsListView.MultiChoiceModeListener 的 API 的正确答案
    • 我无法让它工作,因为我的 ListItems 是RelativeLayouts。有什么建议吗?
    • 谢谢,这是我认为的唯一选择。
    • 嗨@SD你可以试试这个。 '代码' for (int i = 0; i
    【解决方案3】:

    查看 ListView 源代码,解决此问题的唯一方法是将 ListView 设置为 CHOICE_MODE_NONE,然后重新分配 ListAdapter(无论选择模式如何都会清除内部选择列表)

    即在 ListFragment/ListActivity 中

    getListView().setChoiceMode(ListView.CHOICE_MODE_NONE);
    getListView().setAdapter(getListAdapter())
    

    【讨论】:

    • 非常感谢!重新设置适配器就可以了,我怀疑它也兼容所有 API 版本...
    • 一切都很好,除了ListView 会被滚动到最上面的位置
    【解决方案4】:

    我在API Level 17 中遇到了这个问题,并通过以下方式解决了它:

    listView.clearChoices();
    listView.invalidateViews();
    

    【讨论】:

      【解决方案5】:

      对我来说,接受的答案似乎不适用于隐形物品,无需致电

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

      相反,只需调用

      lv.requestLayout();
      

      为了彻底解决我的问题,我打电话给

      lv.clearChoices();
      lv.requestLayout();
      

      onDestroyActionMode()

      然后打电话

      lv.setItemChecked(position, false)
      

      onItemClick() 不处于动作模式时

      但是,我没有确认调用setItemChecked() 是否会导致一些性能问题

      【讨论】:

        【解决方案6】:

        这已被记录为 AOSP bug,但由于某种原因被标记为已过时。

        通常你会认为这会起作用:

        getListView().clearChoices();
        getListView().setChoiceMode(ListView.CHOICE_MODE_NONE);
        

        不幸的是,它没有。在下一个布局过程中将选择模式设置为无将起作用:

        getListView().clearChoices();
        getListView().post(new Runnable() {
            @Override
            public void run() {
                getListView().setChoiceMode(ListView.CHOICE_MODE_NONE);
            }
        });
        

        【讨论】:

          【解决方案7】:

          我已经尝试了上面讨论的所有方法,但它们都不适合我。最后,我决定应用以下解决方法。关键的想法是,

          在多模式期间,我们将创建一个全新的视图,而不是重复使用“缓存”视图。效率不高,但至少“部分”解决了我的问题。

          这是我自定义的ArrayAdapter的代码

          @Override
          public View getView(int position, View convertView, ViewGroup parent) {
              // Key to solve this problem. When we are in multimode, we will not reusing the cached view.
              View rowView = this.multimode ? null : convertView;
          
              if (rowView == null) {
                  LayoutInflater inflater = activity.getLayoutInflater();
                  rowView = inflater.inflate(R.layout.watchlist_row_layout, null);
                  ViewHolder viewHolder = new ViewHolder();
                  viewHolder.textView0 = (TextView) rowView.findViewById(R.id.text_view_0);
                  viewHolder.textView1 = (TextView) rowView.findViewById(R.id.text_view_1);
                  viewHolder.textView2 = (TextView) rowView.findViewById(R.id.text_view_2);
                  rowView.setTag(viewHolder);
              }
          

          另外,我觉得在 ActionMode.Callback 中有以下代码更安全,尽管我不确定它有多大帮助。

              @Override
              public void onDestroyActionMode(ActionMode mode) {
                  MyFragment.this.myArrayAdapter.setMultimode(false);
          
                  // http://stackoverflow.com/questions/9754170/listview-selection-remains-persistent-after-exiting-choice-mode
                  // Using View.post is the key to solve the problem.
                  final ListView listView = MyFragment.this.getListView();
                  listView.clearChoices();
                  for (int i = 0, ei = listView.getChildCount(); i < ei; i++) {
                      listView.setItemChecked(i, false);
                  }
                  listView.post(new Runnable() {
                      @Override
                      public void run() {
                          listView.setChoiceMode(ListView.CHOICE_MODE_NONE);
                      }
                  });
                  actionMode = null;
              }
          

          旁注

          将 MultiChoiceModeListener 与 CHOICE_MODE_MULTIPLE_MODAL 结合使用将消除此错误。但是,对于低于 API 级别 11 的设备将无法使用此解决方案。

          【讨论】:

          • 注意,这并不能完全消除问题。它只是降低了它的发生频率。
          【解决方案8】:

          我知道这已经得到解答,但上面的答案仍然给我带来了 ListView 维护的缓存/回收视图的问题,当滚动回视图时它没有更新它的状态。 所以,上面的解决方案稍微改成:

              lv.clearChoices();  
          
              ArrayList<View> list = new ArrayList<View>();
              lv.reclaimViews(list);
              for (View view : list) {
                  ((Checkable) view).setChecked(false);
              }
          
              lv.setChoiceMode(lv.CHOICE_MODE_NONE);
          

          这比使用 getChildAt(i) 更好,因为该方法 jusg 为您提供当前可见的视图,并且不考虑不可见的内部缓存视图。

          【讨论】:

            【解决方案9】:

            我发现(API 19) 在这里工作的唯一两种方法是:

            • 重置列表适配器,这是不可取的,因为它会回到列表顶部;
            • new Runnable 中将选择模式设置为CHOICE_MODE_NONE

            如果在不使用listView.post(new Runnable()) 的情况下更改了选择模式,则它不起作用。谁能向我解释这是为什么?

            抱歉没有发表评论;我没有名声。

            谢谢。

            【讨论】:

              【解决方案10】:

              不知道这是否为时已晚,只是想分享。我为同一页面创建了一个意图,这样一旦捕获了点击的数据,它就会重新创建一个新页面,而无需任何点击持久性。

              【讨论】:

                【解决方案11】:

                不是错误。该行为是支持 Android 的多个 HID 所必需的。 因此,要显示选择状态,您只需设置列表视图的选择模式和背景以支持“列表项布局”的选择状态,例如:

                android:background="?android:attr/activatedBackgroundIndicator"

                仅供参考:http://android-developers.blogspot.mx/2008/12/touch-mode.html

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2017-09-30
                  • 1970-01-01
                  • 2018-05-13
                  • 1970-01-01
                  • 2021-08-21
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多