IndexOutOfBoundsException при удалении определенных элементов из пользовательского ItemizedOverlay

Уже несколько дней борюсь со следующей проблемой. Я нашел много тем по этой теме, но все немного разные или решения не было.

Для своего проекта я создал собственный файл ItemizedOverlay и добавил его в свой файл MapView. Если я сейчас удалю последний элемент списка элементов, я получу IndexOutOfBoundsException, утверждая, что запрошенный индекс равен размеру ArrayList. т.е. индекс 2 размер 2 или индекс 0 размер 0. Из того, что мне сказали в других темах, я уже пробовал методы populate() и setLastFocusedIndex(-1). Они решили другие проблемы, которые у меня были, но не эту. При удалении других элементов из списка все работает нормально, проблема возникает только для последнего элемента.

Я получаю следующий вывод Logcat:

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)

Что меня беспокоит в этом, так это то, что он, кажется, вызывает методы из стандартного ItemizedOverlay. Я не добавлял обычный ItemizedOverlay и не вызываю суперметоды в своем itemizedOverlay, кроме как в конструкторе. Тем не менее ошибка, кажется, возникает в обычном ItemizedOverlay, для которого имело бы смысл, что ArrayList пусто.

Я надеюсь, что кто-то может указать мне правильное направление, так как я действительно чувствую, что застрял здесь. Заранее спасибо!

Вот мой код:

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;

    }
}

person Pandoro    schedule 24.01.2012    source источник
comment
сделать проверку перед удалением: if (index ‹ listSize) list.remove(index). Лучшее решение — выяснить, почему вы думаете, что у вас есть элементы в списке, хотя на самом деле их нет. Вы прошли?   -  person Adrian    schedule 24.01.2012
comment
разместите свой пользовательский код itemizedOverlay   -  person L7ColWinters    schedule 24.01.2012
comment
@Adrian Адриан, я сделал шаг, и ошибка не возникает при удалении элемента. Все идет гладко, ошибка не возникает до тех пор, пока метод удаления не будет завершен. Вот почему я не думаю, что проверка списка действительно будет полезна. Но я посмотрю еще раз. Спасибо за отзыв.   -  person Pandoro    schedule 24.01.2012


Ответы (3)


Мне потребовалось некоторое время, чтобы понять проблему. Я думаю, что ответ на этот вопрос заключается в том, что ItemizedOverlay не предназначен для управления (добавления и удаления) OverlayItem. Вы можете добавить только OverlayItem к ItemizedOverlay, и если вы хотите изменить какой-либо элемент, вам нужно воссоздать ItemizedOverlay (неэффективно). Чтобы решить эту проблему, вам нужно управлять ItemizedOverlays (всего одним OverlayItem) в списке Overlay вместо OverlayItem в ItemizedOverlay. Что-то такое:

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);
    }
}
person dexity    schedule 28.04.2012
comment
Спасибо за ваш ответ. Звучит хорошо, хотя и звучит немного медленно ;) Но я не хочу делать никаких выводов, не попробовав. В конце концов я использовал другое «решение». Это не элегантно, но работает очень хорошо. Вы должны добавить пустой элемент в конец списка. Это предотвратит возникновение ошибки. Вам просто нужно уделять больше внимания, когда вы добавляете/удаляете элементы. - person Pandoro; 29.04.2012

Получил ту же проблему и обходной путь. Кажется, что код Google вверх (или вниз) по цепочке вызовов становится ужасно запутанным, когда вы сокращаете список OverlayItem в методе onTap. По-видимому, есть какой-то индекс, который не обновляется (или обновляется слишком рано) - отсюда и IOOBE.

Мое решение основано на том факте, что у меня также есть GestureDetector в моем подклассе ItemizedOverlay (я использую onSingleTapConfirmed() для добавления элементов на карту).

  • в функции ItemizedOverlay.onTap(int) сохраните индекс только что нажатого элемента OverlayItem, но не изменяйте список (пока)
  • in onSingleTapConfirmed() check if the 'justTapped' variable is set.
    • If so, remove it from the list
    • вызовите setLastFocusedIndex(-1) и populate() для внутреннего обновления наложения
    • invalidate() MapView для перерисовки.
  • (необязательно) станцуйте счастливый танец, чтобы отпраздновать свой рабочий код.

Надеюсь, это поможет любому!

person edovino    schedule 03.07.2012

У меня такая же проблема. В моем случае я расширил ItemizedOverlay, а внутри хранил массив OverlayItems. Каждый раз, когда я панорамировал или масштабировал карту, я очищал внутренний список элементов, добавлял новые, а затем вызывал заполнение. (На каком-то примере я где-то нашел). Проблема заключалась в том, что базовый класс ItemizedOverlay хранит внутреннюю переменную lastFocusedIndex. После повторного заполнения списка, если он не сброшен - в следующий раз, когда вы решите щелкнуть где-нибудь на карте - он сначала решил снять фокус с lastSelectedItem.... И теперь - если ваш новый список содержит меньше элементов, чем значение из lastSelectedItem - тогда он терпит неудачу с этим исключением.

Решение в моем случае было - после очистки внутреннего списка OverlayItems также вызвать setLastFocusedIndex(-1) - это гарантирует, что все будет сброшено.

Некоторые заметки о том, что мне было полезно для его отладки: 1. Декомпилировал ItemizedOverlay из maps.jar из android-sdk - не помогает, так как это только jar API/Stub, который не содержит реальных классов, используемых @ runtime 2. С помощью DDMS скачал system/framwork/com.google.android.maps.jar 3. преобразовал его с помощью dex2jar, а затем извлек правильный файл класса для декомпиляции

person vlast3k    schedule 23.02.2013