0

I have a ListView with a custom ArrayAdapter and custom items. These items contain multiple View-element, including a Spinner. This Spinner's ArrayAdapter is set like so:

// Method to set or update the Tags in the Spinner
public void updateTagsSpinner(MyHolder h, Spinner sp){
    if(h != null && h.orderedProductItem != null){
        // If the given Spinner null, it means we change the OrderedProductItem's Spinner
        // Is the given Spinner not null, it means we change the Manage Tag's PopupWindow's Spinner
        if(sp == null)
            sp = h.spTags;

        // We know it's an ArrayAdapter<String> so we just ignore the 
        // "Unchecked cast from SpinnerAdapter to ArrayAdapter<String>" warning
        @SuppressWarnings("unchecked")
        ArrayAdapter<String> spAdapt = (ArrayAdapter<String>) sp.getAdapter();
        ArrayList<String> tagStrings = Controller.getInstance().getAllTagsWithOrderedProductItem(h.orderedProductItem));
        if(tagStrings != null && tagStrings.size() > 0){
            if(spAdapt == null){
                spAdapt = new ArrayAdapter<String>(ChecklistActivity.this, android.R.layout.simple_spinner_item, tagStrings);
                spAdapt.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
                // ArrayAdapter's setNotifyOnChange is true by default,
                // but I set it nonetheless, just in case
                spAdapt.setNotifyOnChange(true);
                sp.setAdapter(spAdapt);
            }
            else{
                spAdapt.clear();
                spAdapt.addAll(tagStrings);
            }
        }
        sp.setSelection(h.orderedProductItem.getSelectedFilter());
    }
}

For some reason, every time I scroll down and then up again, my Spinners are completely empty.. And when I click on them I can't even open any Spinners anymore (including the ones that aren't empty) because of a warning:

W/InputEventReceiver(899): Attempted to finish an input event but the input event receiver has already been disposed.

Kevin Cruijssen
  • 9,153
  • 9
  • 61
  • 135

1 Answers1

0

Ok, I found the problem, and it's a pesky one.. That's the reason why I post question/answer as whole to help others with similar problems.

WARNING: adapter.clear() changes the original list that has been used by the ArrayAdapter!!

In my Controller I have a HashMap (allTagsOfProducts) with a key = int productId and a value = ArrayList<String> productTags. In the getAllTagsWithOrderedProductItem-method I use the following:

public ArrayList<String> getAllTagsWithOrderedProductItem(OrderedProductItem opi){
    if(opi != null && opi.getProductId() > 0){
        // Check if a new Tag has been added / removed
        if(getProductById(opi.getProductId()).getTagsHasChanged()){
            ArrayList<String> newProductTags = new ArrayList<String>();

            ... // Do some stuff to fill the newProductTags-List

            // Replace the previously saved Tags of this Products with this updated one
            allTagsOfProduct.put(opi.getProductId(), newProductTags);
            // Set the boolean on false
            getProductById(opi.getProductId()).setTagsHasChanged(false);
            // And return this update list
            return newProductTags;
        }
        // If nothing is added / removed, just return the list we already saved
        else
            return allTagsOfProduct.get(opi.getProductId());
    }
    // If the given OrderedProductItem is null return an empty list
    else
        return new ArrayList<String>();
}

When I debugged this I found out that when I went into the else to the return allTagsOfProduct.get(opi.getProductId()); the first time, everything works fine, and when I went here again, the saved ArrayList inside the HashMap was empty ([]).. The only reason I could think of was the .clear() method of the ArrayAdapter. So instead I had to use a new ArrayList without the reference to the ArrayList in my Controller's HashMap.

TL;DR: The line ArrayList<String> tagStrings = Controller.getInstance().getAllTagsWithOrderedProductItem(h.orderedProductItem)); in my question post is replaced with:

// We create a new ArrayList here, since spAdapt.clear() changes the original list
// all the way into the Controller's HashMap.. You gotta love the Java references.. >.>
ArrayList<String> tagStrings = new ArrayList<String>(
    Controller.getInstance().getAllTagsWithOrderedProductItem(h.orderedProductItem));

PS: The reason why I also couldn't open the non-empty Spinners anymore and it gave me the Warning is because of custom ArrayAdapter's (my ListView's in this case) getView() recycling principle. Click this link for more information of how ListView's getView() recycling works. It re-uses the Item's Spinner for another Item. So even though this new Item's Spinner is filled correctly with the other Item's ArrayList/ArrayAdapter, the Input-EventHandler isn't reset, so I still get this Warning for the non-empty Spinners in the recycling ListView-Item.

Community
  • 1
  • 1
Kevin Cruijssen
  • 9,153
  • 9
  • 61
  • 135