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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9754170/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-16 07:11:27  来源:igfitidea点击:

ListView selection remains persistent after exiting choice mode

javaandroidandroid-listview

提问by CjS

I have a ListView subclass that I allow selections on when the context action bar (CAB) is active. The CAB is set as a callback to the onItemLongClickevent:

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

This is fine, and the ListView works as expected, with the currently selected item staying highlighted when touched.

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

When I close the CAB, I want the ListView to return to normal (i.e. Touch mode). The problem is that the last selected item remains highlighted indefinitely, regardless of what methods I try to clear it:

当我关闭 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;
}

Any suggestions?

有什么建议?

采纳答案by Rudi

The main reason for the problem is that once the ListViewselection mode is switched to CHOICE_MODE_NONE, the framework optimizes out the clearoperation as it is no longer supporting 'selections'. I have improved the above workarounds a bit by clearing the selection state manually and then setting the mode in a delayed manner so the framework will have its turn to clear the state before turning the mode to CHOICE_MODE_NONE.

问题的主要原因是,一旦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);
    }
});

回答by Knickedi

I faced the same issue and since requesting layout doesn't solve the problem for me either I implemented a little hack which works for me. Maybe this is the same issue because I'm switching between CHOICE_MODE_SINGLEand CHOICE_MODE_NONE.

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

When the action mode ends I'm calling this code snippet. clearChoicesmakes sure that all items are not checked anymore (internally). The iteration over the views makes sure that all currently visible views are reset and not checked anymore.

当动作模式结束时,我正在调用这个代码片段。clearChoices确保不再检查所有项目(内部)。视图上的迭代确保所有当前可见的视图都被重置并且不再被检查。

mListView.clearChoices();

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

mListView.setChoiceMode(ListView.CHOICE_MODE_NONE);

回答by Migaloo

Looking at the ListView sourcecode, the only way to work around this is to set the ListView to CHOICE_MODE_NONE, then re-assign the ListAdapter (which clears the internal selection list regardless of choice mode)

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

i.e. in a ListFragment/ListActivity

即在 ListFragment/ListActivity 中

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

回答by Cheok Yan Cheng

I had tried all the approaches discussed above but none of them work for me. Finally, I decide to apply the following workaround. The key idea is that,

我已经尝试了上面讨论的所有方法,但没有一个对我有用。最后,我决定应用以下解决方法。关键思想是,

During multimode, instead of reusing the "cached" view, we will create a completely new view. Not efficient, but at least "partially" solve my problem.

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

Here is the code of my customized ArrayAdapter

这是我定制的代码 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);
    }

Also, I feel safer to have the following code in ActionMode.Callback, although I'm not sure how much it helps.

另外,我觉得在 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;
    }

Side Note

边注

Using MultiChoiceModeListener couple with CHOICE_MODE_MULTIPLE_MODAL will make this bug gone. However, for device below API level 11 will not able to use this solution.

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

回答by Daniel De León

Is not a bug. That behavior is required to support multiple HIDfor Android. So to show the selection state you only need set the choice mode of the listview and a background to support the selected state for the "list item layout", like:

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

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

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

FYI: http://android-developers.blogspot.mx/2008/12/touch-mode.html

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

回答by jpmcosta

I was having this issue in API Level 17and solved it by doing:

我遇到了这个问题API Level 17并通过执行以下操作解决了它:

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

回答by anjosc

I know this has been answered, but above answers still gave me problems with the cached/recycled views that ListView maintains, that didn't update it's state when scrolled back into view. So, the above solution changes slightly to:

我知道这已经得到了回答,但是上面的答案仍然给我带来了 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);

This is better than using getChildAt(i) because that method jusg gives you the currently visble views and does not account for the internal cached views, that are not visible.

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

回答by mdhvn

I have found that the only two methods that work here (API 19)are:

我发现这里唯一有效的两种方法(API 19)是:

  • Resetting the list adapter, which is undesirable because it goes back to the top of the list;
  • Setting the choice mode to CHOICE_MODE_NONEin a new Runnable
  • 重置列表适配器,这是不可取的,因为它回到了列表的顶部;
  • 将选择模式设置CHOICE_MODE_NONEnew Runnable

If the choice mode is changed without using listView.post(new Runnable()), it doesn't work. Can anyone explain to me why this is?

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

Apologies for not commenting; I have no reputation.

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

Thanks.

谢谢。

回答by darktiny

For me, it seems the accepted answer is not working for invisible items, and it's no need to call

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

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

instead, just call

相反,只需致电

lv.requestLayout();

To completely solve my issue, I call

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

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

in onDestroyActionMode()

onDestroyActionMode()

and call

并打电话

lv.setItemChecked(position, false)

in onItemClick()when it's not in ActionMode

onItemClick()它不处于 ActionMode 时

However, I did not confirm whether call setItemChecked()will result some performance issues

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

回答by Critical_Eye

Not sure if this is too late just wanted to share. I created an intent to the same page so that once the clicked data is captured it recreates a fresh page without any clicked persistence.

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