androidandroid-mapviewitemizedoverlayindexoutofboundsexception

IndexOutOfBoundsException when removing certain items from custom ItemizedOverlay


I've been struggling with the following problem for some days now. I have found many threads regarding this topic, but all are a little different or there was no solution.

For my project I created a custom ItemizedOverlay and added this to my MapView. If I now remove the last item of the list of items I get an IndexOutOfBoundsException claiming that the requested index is equal to the size of the ArrayList. I.e. index 2 size 2 or index 0 size 0. From what other topics have told me I have already tried the populate() and setLastFocusedIndex(-1) methods. These have solved other problems I had, but not this one. When removing other items from the list it works fine, the problem only seems to occur for the last item.

I get the following Logcat output:

01-24 16:11:08.091: E/AndroidRuntime(916): Uncaught handler: thread main exiting due to uncaught exception
01-24 16:11:08.101: E/AndroidRuntime(916): java.lang.IndexOutOfBoundsException: Invalid location 0, size is 0
01-24 16:11:08.101: E/AndroidRuntime(916):  at java.util.ArrayList.get(ArrayList.java:341)
01-24 16:11:08.101: E/AndroidRuntime(916):  at com.google.android.maps.ItemizedOverlay.getItem(ItemizedOverlay.java:419)
01-24 16:11:08.101: E/AndroidRuntime(916):  at com.google.android.maps.ItemizedOverlay.focus(ItemizedOverlay.java:538)
01-24 16:11:08.101: E/AndroidRuntime(916):  at com.google.android.maps.ItemizedOverlay.onTap(ItemizedOverlay.java:455)
01-24 16:11:08.101: E/AndroidRuntime(916):  at com.google.android.maps.OverlayBundle.onTap(OverlayBundle.java:83)
01-24 16:11:08.101: E/AndroidRuntime(916):  at com.google.android.maps.MapView$1.onSingleTapUp(MapView.java:346)
01-24 16:11:08.101: E/AndroidRuntime(916):  at android.view.GestureDetector.onTouchEvent(GestureDetector.java:506)
01-24 16:11:08.101: E/AndroidRuntime(916):  at com.google.android.maps.MapView.onTouchEvent(MapView.java:628)
01-24 16:11:08.101: E/AndroidRuntime(916):  at android.view.View.dispatchTouchEvent(View.java:3709)
01-24 16:11:08.101: E/AndroidRuntime(916):  at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:852)
01-24 16:11:08.101: E/AndroidRuntime(916):  at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884)
01-24 16:11:08.101: E/AndroidRuntime(916):  at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884)
01-24 16:11:08.101: E/AndroidRuntime(916):  at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884)
01-24 16:11:08.101: E/AndroidRuntime(916):  at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884)
01-24 16:11:08.101: E/AndroidRuntime(916):  at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1659)
01-24 16:11:08.101: E/AndroidRuntime(916):  at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1107)
01-24 16:11:08.101: E/AndroidRuntime(916):  at android.app.Activity.dispatchTouchEvent(Activity.java:2061)
01-24 16:11:08.101: E/AndroidRuntime(916):  at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1643)
01-24 16:11:08.101: E/AndroidRuntime(916):  at android.view.ViewRoot.handleMessage(ViewRoot.java:1691)
01-24 16:11:08.101: E/AndroidRuntime(916):  at android.os.Handler.dispatchMessage(Handler.java:99)
01-24 16:11:08.101: E/AndroidRuntime(916):  at android.os.Looper.loop(Looper.java:123)
01-24 16:11:08.101: E/AndroidRuntime(916):  at android.app.ActivityThread.main(ActivityThread.java:4363)
01-24 16:11:08.101: E/AndroidRuntime(916):  at java.lang.reflect.Method.invokeNative(Native Method)
01-24 16:11:08.101: E/AndroidRuntime(916):  at java.lang.reflect.Method.invoke(Method.java:521)
01-24 16:11:08.101: E/AndroidRuntime(916):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
01-24 16:11:08.101: E/AndroidRuntime(916):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
01-24 16:11:08.101: E/AndroidRuntime(916):  at dalvik.system.NativeStart.main(Native Method)

What bothers me about this is that it seems to be calling methods from the standard ItemizedOverlay. I have not added a normal ItemizedOverlay and I don't call super methods in my itemizedOverlay except in the constructor. Still the error seems to occur in a normal ItemizedOverlay, for which it would make sense that the ArrayList is empty.

I hope someone can point me in the right direction since I really feel stuck here. Thanks in advance!

Here is my code:

public class GameItemOverlay extends ItemizedOverlay<Item> {

    private ArrayList<Item> mOverlays = new ArrayList<Item>();


    public GameItemOverlay(Drawable defaultMarker) {
        super(boundCenterBottom(defaultMarker));
        setLastFocusedIndex(-1);
        populate(); 
    }

    public void itemDataReady(){
        mOverlays = GameSession.items;
        setLastFocusedIndex(-1);
        populate();
    }

    @Override
    protected Item createItem(int i) {
      return mOverlays.get(i);
    }

    @Override
    public int size() {
      return mOverlays.size();
    }

    @Override
    protected boolean onTap(int i){
        GameSession.remove(mOverlays.get(i).getID()); //Removes the item according to it's position

        setLastFocusedIndex(-1);
        populate();   
        return true;

    }
}

Solution

  • It took me a while to figure the problem. I think the answer to this question is that ItemizedOverlay is not intended to be used to manage (add and remove) OverlayItems. You can pretty much only add OverlayItems to ItemizedOverlay and if you want to change any item, you need to recreate ItemizedOverlay (not efficient). To solve this problem you need to manage ItemizedOverlays (with just one OverlayItem) in list of Overlays instead of OverlayItems in ItemizedOverlay. Something like that:

    public class SomeActivity extends MapActivity {
    
        private SomeMapView mView;
    
        // Rest of the code ...
    
        private void sendRequest(GeoPoint point)
        {
            List<Overlay> mOverlays = mView.getOverlays();
            // Show red marker
            OverlayItem item    = new OverlayItem(point, "Hello", "world");   
            MarkerItemizedOverlay itemOverlay   = new MarkerItemizedOverlay(drawable, mView);
            itemOverlay.addOverlay(item);
            mOverlays.add(itemOverlay);
        }
    }
    
    public class MarkerItemizedOverlay extends ItemizedOverlay<OverlayItem> {
    
        private ArrayList<OverlayItem> mOverlays = new ArrayList<OverlayItem>();
        MapView mView;
    
        // Rest of the code ...
    
        @Override
        protected boolean onTap(int index) {
            List<Overlay> mOverlays = mView.getOverlays();
            mOverlays.remove(this);
        }
    }