📜 ⬆️ ⬇️

AlertDialog setMultiChoiceItems, bug or non-obvious feature

Hi, Habr!

Introduction


The last couple of months working on a single project for Android. But now it’s not about him, I’ll try to write about him, but everything has its time

For all the time of work on the project, a lot of interesting things happened (and happen). Today I want to tell a little story.

Beginning of the story


One evening, just before the New Year holidays, working in the office and drinking a favorite tea, I wrote another part of the functionality for the project.
')
It took me to create a normal dialogue, with the possibility of multiple selection of elements from the list, with the ability to immediately mark / uncheck all elements, save the selected elements and mark them immediately after the subsequent display of the dialog.
No sooner said than done.

Actually the code itself:
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(); } 


It would seem that the problem is solved, you can test and go with a clear conscience to rest (well, okay, play WOT =)).

The button to mark / unmark all elements worked. But then I noticed, if I first marked a couple of elements, then with the button to mark all the elements and immediately remove the mark from all, then the couple of elements that I marked myself remain marked.
Having a little experimented, I decided that it was time to go to rest and continue exploring in the morning. Before leaving, I raised this question on stackoverflow .

In the morning, having come to work, having reviewed his topic and not having seen any practical advice, I continued the analysis further.
After not a long analysis of what is happening, it became clear that the problem is reproduced only if
setMultiChoiceItems(CharSequence[] items, boolean[] checkedItems, OnMultiChoiceClickListener listener)
pass the array checkedItems, if null was passed instead, the problem did not occur.
I raised the question again on stackoverflow , but unfortunately no one advised anything.

It was already December 29 in the courtyard, and the project still had many tasks, so by rewriting the code a little bit, without transferring the array, the problem was solved.
Reporting a problem to Google’s bugtracker and continuing to work further, I forgot about this story.

Our days


Today, looking through the mail, I saw that someone had responded to my bug report. After viewing it became clear that a couple more people faced the same problem.

Having dealt with things a little, I decided to look at the problem again.
An idea came to mind, although it seemed to me that she had already arrived and I checked it, I checked it again.
The essence of the idea was to make the array checkedItems a field of the class, and in the listener to work with it too.
As a result, the onClick method took the form:

 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)); } } 


And yes, that solved the original problem.

Total.


It can be concluded, although I do not like it:
If the state of an element in the checkedItems array is indicated as not marked (false), then when checked, it is marked on the screen (but not marked in the array) and when unchecked, the mark is removed.
But if in the checkedItems array the state of the element is indicated as marked (true), then when unchecking the mark, the element still remains marked on the screen, since it remains marked in the array.

This is the story that turned out, I hope it was interesting and you did not waste your time, however, like me.
Thank you for your attention, if you have questions, I will be happy to answer.
In the next article I want to talk about the project itself, it should be interesting.

Source: https://habr.com/ru/post/139322/


All Articles