Привет!
Вступление
Последние пару месяцев работаю над одним проектом под Android. Но речь сейчас пойдет не о нем, о нем я постараюсь обязательно написать, но всему свое время
За все время работы над проектом, случалось (и случается) много интересного. Сегодня я хочу рассказать одну небольшую историю.
Начало истории
Как-то вечером, совсем перед новогодними праздниками, работая в офисе и попивая любимый чаек, писал я очередную часть функционала для проекта.
Понадобилось мне создать обычный диалог, с возможностью множественного выбора элементов из списка, с возможностью сразу отметитьснять отметку со всех элементов, сохранить выбранные элементы и при последующим отображением диалога сразу их отметить.
Сказано — сделано.
Собственно сам код:
protected ArrayList<String> items; protected ArrayList<String> selectedItems; protected void showMyDialog() { int count = items.size(); boolean[] checkedItems = new boolean[count]; for (int i = 0; i < count; i++) checkedItems[i] = selectedItems.contains(items.get(i)); AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMultiChoiceItems(items.toArray(new String[items.size()]), checkedItems, new DialogInterface.OnMultiChoiceClickListener() { @Override public void onClick(DialogInterface dialog, int which, boolean isChecked) { ListView list = ((AlertDialog) dialog).getListView(); if (isChecked) { if (which == 0) { for (int i = 0; i < list.getCount(); ++i) list.setItemChecked(i, true); selectedItems.clear(); selectedItems.addAll(items); } else selectedItems.add(items.get(which)); } else { if (which == 0) { for (int i = 0; i < list.getCount(); ++i) list.setItemChecked(i, false); selectedItems.clear(); } else selectedItems.remove(items.get(which)); } } }); AlertDialog dialog = builder.create(); dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); dialog.show(); }
Казалось бы, проблема решена, можно протестировать и идти с чистой совестью отдыхать(ну ладно, поиграть в WOT=) ).
Кнопка отметитьснять отметку со всех элементов работала. Но потом я заметил, если отметить вначале самому пару элементов, потом кнопкой отметить все элементы и сразу же снять отметку со всех, то те пару элементов, которые отмечал сам, остаются отмеченными.
Немного поэкспериментировав, решил, что пора идти отдыхать и продолжить разбираться утром. Перед уходом поднял этот вопрос на stackoverflow.
Утром придя на работу, просмотрев свою тему и не увидев никаких дельных советов, продолжил разбор дальше.
После не продолжительного анализа происходящего, стало понятно, что проблема воспроизводится, только если в функцию setMultiChoiceItems(CharSequence[] items, boolean[] checkedItems,
DialogInterface.OnMultiChoiceClickListener listener)
передавать массив checkedItems, если вместо него передавал null, проблема не возникала.
Поднял снова вопрос на stackoverflow, но к сожалению никто так ничего и не посоветовал.
На дворе уже было 29 декабря, да и по проекту еще было много задач, поэтому переписав немного код, без передачи массива, проблема была решена.
Сообщив о найденной проблеме в багтрекер гугла и продолжив работу дальше, я и забыл про эту историю.
Наши дни
Сегодня, просматривая почту, увидел, что кто-то откликнулся на мой багрепорт. После просмотра стало понятно, что еще пару человек столкнулись с этой же проблемой.
Разобравшись немного с делами, решил ещё раз взглянуть на проблему.
В голову пришла идея, хотя мне казалось, что она уже приходила и я её проверял, я проверил еще раз.
Суть идеи заключалась в том, чтобы массив checkedItems сделать полем класса, и в слушателе работать и с ним тоже.
В итоге, метод onClick принял вид:
public void onClick(DialogInterface dialog, int which, boolean isChecked) { ListView list = ((AlertDialog) dialog).getListView(); if (isChecked) { if (which == 0) { for (int i = 0; i < list.getCount(); ++i) { list.setItemChecked(i, true); checkedItems[i]=true; } selectedItems.clear(); selectedItems.addAll(items); } else selectedItems.add(items.get(which)); } else { if (which == 0) { for (int i = 0; i < list.getCount(); ++i) { list.setItemChecked(i, false); checkedItems[i]=false; } selectedItems.clear(); } else selectedItems.remove(items.get(which)); } }
И таки да, это решило первоначальную проблему.
Итог.
Можно сделать вывод, хоть он мне и не нравится:
Если в массиве checkedItems состояние элемента указано как не отмеченное (false), то при отметки он отмечается на экране (но не отмечается в массиве) и при снятии отметки, отметка снимается.
Но если в массиве checkedItems состояние элемента указано как отмеченное (true), то при снятии отметки, элемент все равно остается отмеченным на экране, т.к. он остается отмеченным в массиве.
Вот такая получилась история, надеюсь было интересно и вы не зря потратили время, впрочем как и я.
Спасибо за внимание, если есть вопросы, с радостью отвечу.
В следующей статье хочу рассказать уже о самом проекте, должно быть интересно.
Автор: silentnuke