Привет, Хабр!
Последние пару месяцев работаю над одним проектом под Android. Но речь сейчас пойдет не о нем, о нем я постараюсь обязательно написать, но всему свое время
За все время работы над проектом, случалось (и случается) много интересного. Сегодня я хочу рассказать одну небольшую историю.
Как-то вечером, совсем перед новогодними праздниками, работая в офисе и попивая любимый чаек, писал я очередную часть функционала для проекта.
Понадобилось мне создать обычный диалог, с возможностью множественного выбора элементов из списка, с возможностью сразу отметить\снять отметку со всех элементов, сохранить выбранные элементы и при последующим отображением диалога сразу их отметить.
Сказано — сделано.
Собственно сам код:
Казалось бы, проблема решена, можно протестировать и идти с чистой совестью отдыхать(ну ладно, поиграть в WOT=) ).
Кнопка отметить\снять отметку со всех элементов работала. Но потом я заметил, если отметить вначале самому пару элементов, потом кнопкой отметить все элементы и сразу же снять отметку со всех, то те пару элементов, которые отмечал сам, остаются отмеченными.
Немного поэкспериментировав, решил, что пора идти отдыхать и продолжить разбираться утром. Перед уходом поднял этот вопрос на stackoverflow.
Утром придя на работу, просмотрев свою тему и не увидев никаких дельных советов, продолжил разбор дальше.
После не продолжительного анализа происходящего, стало понятно, что проблема воспроизводится, только если в функцию
передавать массив checkedItems, если вместо него передавал null, проблема не возникала.
Поднял снова вопрос на stackoverflow, но к сожалению никто так ничего и не посоветовал.
На дворе уже было 29 декабря, да и по проекту еще было много задач, поэтому переписав немного код, без передачи массива, проблема была решена.
Сообщив о найденной проблеме в багтрекер гугла и продолжив работу дальше, я и забыл про эту историю.
Сегодня, просматривая почту, увидел, что кто-то откликнулся на мой багрепорт. После просмотра стало понятно, что еще пару человек столкнулись с этой же проблемой.
Разобравшись немного с делами, решил ещё раз взглянуть на проблему.
В голову пришла идея, хотя мне казалось, что она уже приходила и я её проверял, я проверил еще раз.
Суть идеи заключалась в том, чтобы массив checkedItems сделать полем класса, и в слушателе работать и с ним тоже.
В итоге, метод onClick принял вид:
И таки да, это решило первоначальную проблему.
Можно сделать вывод, хоть он мне и не нравится:
Если в массиве checkedItems состояние элемента указано как не отмеченное (false), то при отметки он отмечается на экране (но не отмечается в массиве) и при снятии отметки, отметка снимается.
Но если в массиве checkedItems состояние элемента указано как отмеченное (true), то при снятии отметки, элемент все равно остается отмеченным на экране, т.к. он остается отмеченным в массиве.
Вот такая получилась история, надеюсь было интересно и вы не зря потратили время, впрочем как и я.
Спасибо за внимание, если есть вопросы, с радостью отвечу.
В следующей статье хочу рассказать уже о самом проекте, должно быть интересно.
Вступление
Последние пару месяцев работаю над одним проектом под 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, 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), то при снятии отметки, элемент все равно остается отмеченным на экране, т.к. он остается отмеченным в массиве.
Вот такая получилась история, надеюсь было интересно и вы не зря потратили время, впрочем как и я.
Спасибо за внимание, если есть вопросы, с радостью отвечу.
В следующей статье хочу рассказать уже о самом проекте, должно быть интересно.