Подтвердить что ты не робот

Выбор 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;
}

Любые предложения?

4b9b3361

Ответ 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);
    }
});

Ответ 2

Я столкнулся с той же проблемой, и поскольку запрос макета не решает проблему для меня, либо я реализовал небольшой хак, который работает для меня. Возможно, это та же проблема, потому что я переключаюсь между CHOICE_MODE_SINGLE и CHOICE_MODE_NONE.

Когда закон заканчивается, я вызываю этот фрагмент кода. clearChoices гарантирует, что все элементы больше не будут проверяться (внутренне). Итерация над представлениями гарантирует, что все видимые в настоящее время виды reset и больше не отмечены.

mListView.clearChoices();

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

mListView.setChoiceMode(ListView.CHOICE_MODE_NONE);

Ответ 3

Посмотрев на исходный код ListView, единственный способ обойти это - установить ListView в CHOICE_MODE_NONE, а затем повторно назначить ListAdapter (который очищает внутренний список выбора независимо от режима выбора)

то есть. в ListFragment/ListActivity

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

Ответ 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(), если он не находится в ActionMode

Однако я не подтвердил, вызовет ли вызов setItemChecked() некоторые проблемы с производительностью

Ответ 6

Это зарегистрировано как ошибка AOSP, но по какой-либо причине помечается как устаревшая.

Обычно вы ожидаете, что это сработает:

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):

  • Сброс адаптера списка, что нежелательно, поскольку оно возвращается в начало списка;
  • Установка режима выбора CHOICE_MODE_NONE в new Runnable

Если режим выбора изменен без использования listView.post(new Runnable()), он не работает. Может ли кто-нибудь объяснить мне, почему это так?

Извинения за не комментирование; У меня нет репутации.

Спасибо.

Ответ 10

Не уверен, что слишком поздно просто хочет поделиться. Я создал намерение на той же странице, чтобы после захвата данных щелкнула новую страницу без какой-либо нажатой настойчивости.

Ответ 11

Не является ошибкой. Это поведение требуется для поддержки нескольких HID для Android. Таким образом, чтобы показать состояние выбора, вам нужно установить режим выбора списка и фон для поддержки выбранного состояния для "макета списка элементов", например:

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

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