1

I have an AlertDialog with a ListView set to multiple selection on it. It also has a Button on it.

The Button open another AlertDialog that if ok'ed will remove the selected items from the data set of the ListView, and then tell the adapter of the list view that the dataset has changed with the notifyDataSetChanged() method.

This all works fine except for one thing. The ListView does not update it's content until I interact with something. Then it updates to the correct data.

This is not a big problem, but I really would like the ListView to appear correct at once, and not just after the focus has changed.

Code:

Button remove = (Button) view.findViewById(R.id.btn_remove_questions_edit_rack);
    final Context con = this;
    remove.setOnClickListener(new OnClickListener()
    {
        @Override
        public void onClick(View v)
        {
            Builder warnBuild = new Builder(con);
            warnBuild.setMessage(R.string.question_deletion_warning);
            warnBuild.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener()
            {
                @Override
                public void onClick(DialogInterface dialog, int which)
                {
                    SparseBooleanArray checked = list.getCheckedItemPositions();
                    for (String s : keys)
                    {
                        int i = keys.indexOf(s);
                        if (checked.get(i))
                        {
                            toRemove.add(map.get(s));
                            map.remove(s);
                        }
                    }
                    keys.clear();
                    keys.addAll(map.keySet());
                    ((ArrayAdapter) list.getAdapter()).notifyDataSetChanged();
                    list.clearChoices(); //This makes sure the selection is cleared, if it isn't, some of the other items (those that now has the index of the selected items) will be selected when the View refreshes.
                    dialog.dismiss();
                }
            });

            //Negative button here, not relevant.
        }
    });

Where map and keys are:

final HashMap<String, QualityQuestion> map = new HashMap<>();
//I add items to the map
final ArrayList<String> keys = new ArrayList<>(map.keySet());

And toRemove is where I store the items to be removed from the actual object they are on when the ok button on the original AlertDialog is pressed.

This is how I populate my ListView in the first place:

final ListView list = (ListView) view.findViewById(R.id.list_questions_edit_rack);
    list.setAdapter(
            new ArrayAdapter<String>(this,
                    android.R.layout.simple_list_item_activated_1,
                    keys));

I have tried things like list.invalidateViews(), list.invalidate and other things I found in questions similar to mine here on SO. But none of that made any difference. I suspect my problem to be different from theirs since my items clearly are updated, it just takes a change of focus on the original AlertDialog for the change to be visible.

How can I make the ListView show the changes in it's data source imidiatly insted of after a focus change?

Kitalda
  • 351
  • 2
  • 18

2 Answers2

3

By calling

((ArrayAdapter) list.getAdapter()).notifyDataSetChanged();

you get a fresh adapter which is almost certainly not identical to the anonymous adapter you used to populate your list in the first instance.

See also the documentation for ListView.getAdapter()

Returns the adapter currently in use in this ListView. The returned adapter might not be the same adapter passed to setAdapter(ListAdapter) but might be a WrapperListAdapter.

From the point of view of this fresh adapter, the data set hasn't changed because the changes happened way before it was instantiated.

To solve your problem, make your list and your list adapter members of your activity class (or the scope where you want to keep them alive):

private ArrayList<String>    keys;
private ArrayAdapter         myAdapter;
private ListView             list;

Then in your "onCreate()"

keys = ...;     // initialization of ArrayList with the needed data 
myAdapter = new ArrayAdapter<String>(this,
                    android.R.layout.simple_list_item_activated_1,
                    keys);
list = (ListView) view.findViewById(R.id.list_questions_edit_rack);
list.setAdapter(myAdapter);

This way, in your "OnClickListener" you can notify "myAdapter":

keys.addAll(map.keySet());
myAdapter.notifyDataSetChanged();

Hope this helps :)

Bö macht Blau
  • 12,820
  • 5
  • 40
  • 61
0

You can tweak it, by granting focus to another view, and then requesting it back:

view.requestFocus();

You can also use:

view.requestFocusFromTouch();
Sahar Avr
  • 131
  • 8
  • Nope, does not work. Not with the `View` that contains all the elements at least. – Kitalda Aug 05 '15 at 11:38
  • Nor does requesting focus to the `Button` that is pressed. – Kitalda Aug 05 '15 at 11:40
  • or to the ListView. Or to the button and then the ListView. – Kitalda Aug 05 '15 at 11:42
  • Btw, this way the problem with another item being selected reappears even though I solved it with `list.clearChoices()` – Kitalda Aug 05 '15 at 11:44
  • When the button is pressed it should get the focus. How come after your button is pressed the listView gets the focus? – Sahar Avr Aug 05 '15 at 15:42
  • Also, never pass `this` as context. Use `getApplicationContext()` instead. – Sahar Avr Aug 05 '15 at 15:44
  • I don't know if the ListView has focus or not, just that I need to tap somewhere to give something focus for the change to be shown. – Kitalda Aug 05 '15 at 18:40
  • And I'll change the Context when I'm back to work, then :) – Kitalda Aug 05 '15 at 18:41
  • Actually, using `getApplicationContext()` triggers an exception. That is a known bug (see [Android 1.6: “android.view.WindowManager$BadTokenException: Unable to add window — token null is not for an application”](http://stackoverflow.com/questions/2634991/android-1-6-android-view-windowmanagerbadtokenexception-unable-to-add-window)) – Kitalda Aug 11 '15 at 06:55