退出选择模式后,ListView选择保持不变

时间:2012-03-17 22:04:44

标签: java android android-listview

我有一个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;
}

有什么建议吗?

11 个答案:

答案 0 :(得分:34)

问题的主要原因是,ListView选择模式切换为CHOICE_MODE_NONE后,框架会优化清除操作,因为它不再支持'选择。我通过手动清除选择状态然后以延迟方式设置模式来改进上述变通方法,以便在将模式转换为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);
    }
});

答案 1 :(得分:20)

我遇到了同样的问题,因为请求布局并没有解决我的问题,要么我实施了一个对我有用的小黑客。也许这是同一个问题,因为我在CHOICE_MODE_SINGLECHOICE_MODE_NONE之间切换。

当动作模式结束时,我正在调用此代码段。 clearChoices确保不再检查所有项目(内部)。对视图的迭代可确保重置所有当前可见的视图,而不再进行检查。

mListView.clearChoices();

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

mListView.setChoiceMode(ListView.CHOICE_MODE_NONE);

答案 2 :(得分:18)

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

即。在ListFragment / ListActivity中

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

答案 3 :(得分:4)

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

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

答案 4 :(得分:2)

对我而言,似乎接受的答案不适用于隐形物品,而且无需拨打电话

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

相反,只需致电

lv.requestLayout();

要彻底解决我的问题,请致电

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

中的

并致电

lv.setItemChecked(position, false)
<{1>}中的

,当它不在ActionMode

中时

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

答案 5 :(得分:1)

这已记录为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);
    }
});

答案 6 :(得分:0)

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

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

以下是我自定义的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级将无法使用此解决方案。

答案 7 :(得分:0)

我知道这已经得到了解答,但上面的答案仍然给我带来了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为您提供当前可见的视图,并且不考虑不可见的内部缓存视图。

答案 8 :(得分:0)

我发现在(API 19)中使用的唯一两种方法是:

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

如果在不使用listView.post(new Runnable())的情况下更改了选择模式,则无效。任何人都可以向我解释为什么会这样吗?

不发表评论的道歉;我没有声誉。

感谢。

答案 9 :(得分:0)

不确定这是否为时已晚只想分享。我为同一页面创建了一个intent,这样一旦捕获了单击的数据,它就会重新创建一个没有任何点击持久性的新页面。

答案 10 :(得分:-2)

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

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

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