bill在前文说道Android Google Map API中有很多小陷阱,本文将介绍bill在开发过程中遇到的其中一个并给出解决方案。

    通过前面的介绍,我们知道可以通过继承Android Google Map API中的ItemizedOverlay来自定义我们需要的叠加层。既然是自定义叠加层,就应该为它编写“添加”、“删除”以及“清空”等常用功能,于是我们前面编写的HelloItemizedOverlay可以改写为下面的样子

   
   
   
   
  1. import android.content.Context; 
  2. import android.graphics.Canvas; 
  3. import android.graphics.drawable.Drawable; 
  4.  
  5. import com.google.android.maps.ItemizedOverlay; 
  6. import com.google.android.maps.MapView; 
  7. import com.google.android.maps.OverlayItem; 
  8.  
  9. @SuppressWarnings("rawtypes"
  10. public class HelloItemizedOverlay extends ItemizedOverlay { 
  11.     private ArrayList mOverlayItems = new ArrayList(); 
  12.     private Context mContext = null
  13.      
  14.     public HelloItemizedOverlay(Drawable defaultMarker) { 
  15.         super(boundCenterBottom(defaultMarker)); 
  16.     } 
  17.      
  18.     public HelloItemizedOverlay(Drawable defaultMarker, Context context) { 
  19.         super(boundCenterBottom(defaultMarker)); 
  20.         mContext = context; 
  21.     } 
  22.      
  23.     @Override 
  24.     protected OverlayItem createItem(int i) { 
  25.         OverlayItem item = null
  26.         try { 
  27.             item = mOverlayItems.get(i); 
  28.         } catch (ArrayIndexOutOfBoundsException e) { 
  29.             e.printStackTrace(); 
  30.         } catch (IndexOutOfBoundsException e) { 
  31.             e.printStackTrace(); 
  32.         } catch (Exception e) { 
  33.             e.printStackTrace(); 
  34.         } 
  35.         return item; 
  36.     } 
  37.      
  38.     @Override 
  39.     public int size() { 
  40.         return mOverlayItems.size(); 
  41.     } 
  42.      
  43.     @Override 
  44.     protected boolean onTap(int index) { 
  45.       return true
  46.     } 
  47.      
  48.     /** 
  49.      * add new overlay item into overlay items array 
  50.      *  
  51.      * @param overlay 
  52.      */ 
  53.     public void addOverlay(OverlayItem overlay) { 
  54.         try { 
  55.             mOverlayItems.add(overlay); 
  56.             populate(); 
  57.         } catch (Exception e) { 
  58.             e.printStackTrace(); 
  59.         } 
  60.     } 
  61.      
  62.     /** 
  63.      * remove overlay item 
  64.      */ 
  65.     public void remove(OverlayItem overlayItem) { 
  66.         try { 
  67.             mOverlayItems.remove(overlayItem); 
  68.             populate(); 
  69.         } catch (Exception e) { 
  70.             e.printStackTrace(); 
  71.         } 
  72.     } 
  73.      
  74.     /** 
  75.      * clear overlay 
  76.      */ 
  77.     public void clear() { 
  78.         try { 
  79.             mOverlayItems.clear(); 
  80.             populate(); 
  81.         } catch (Exception e) { 
  82.             e.printStackTrace(); 
  83.         } 
  84.     } 

     非常简单的逻辑,看似毫无破绽,于是bill就兴高采烈地用这个“陷阱工具”进行程序的开发了,开始还好,我只是示意性地往叠加层中添加1~2个图标,程序安然无恙地运行着。也怪自己大意,没有一点对它进行测试的意识,就这样带着陷阱一直开发,直到我有必要向叠加层添加更多的图标并根据程序逻辑进行删除时,才发现程序总是莫名其妙地崩溃,要么得到ArrayOutOfBoundsException,要么得到NullPointerException,于是bill开始各种try...catch...可惜手头并无Google源码,异常捕获工作也只是徒劳无功。

    来回辗转几天,最后在国外一篇文章中找到了解决办法,当时只有一个念头,Google MAP API document真心不给力......

    根据google自己的doc说明,每次对Overlay List中的数据进行更新后,需要调用populate()方法,以便将更新同步到UI,然而,事实远非如此,ItemizedOverlay这个类自己还记录了最后一个具有焦点的OverlayItem的下标,并且还使用这个下标维持诸如lastFocusedIndex等内部数据,而当我们更新(增、删)Overlay列表时,ItemizedOverlay并不保证lastFocuedIndex的正确性,因此当ItemizedOverlay内部再次使用已经不正确的lastFocusedIndex时,一切就可想而知了。

    解决办法就是在我们每次更新之后,主动地设置lastFocusedIndex的值为-1,表示从来没有哪一个item在最近获得过焦点,这将使得其内部调用nextFocus(boolean forwords)时返回列表中的第一个item或者null,如此便能直接消除该异常或产生我们可以在自己代码中捕获并进行相关处理的异常。

    其次,当我们不向Overlay列表中添加任何项就直接显示时,还会得到NullPointerException,异常源于google的一个bug,当时被标注为未修复,解决办法是在构造函数的末尾调用populate()方法。

    根据上述说明,代码修改如下: 

   
   
   
   
  1. import android.content.Context; 
  2. import android.graphics.Canvas; 
  3. import android.graphics.drawable.Drawable; 
  4.  
  5. import com.google.android.maps.ItemizedOverlay; 
  6. import com.google.android.maps.MapView; 
  7. import com.google.android.maps.OverlayItem; 
  8.  
  9. @SuppressWarnings("rawtypes"
  10. public class HelloItemizedOverlay extends ItemizedOverlay { 
  11.     private ArrayList mOverlayItems = new ArrayList(); 
  12.     private Context mContext = null
  13.      
  14.     public HelloItemizedOverlay(Drawable defaultMarker) { 
  15.         super(boundCenterBottom(defaultMarker)); 
  16.          
  17.         //fix the NullPointerException bug 
  18.         populate(); 
  19.     } 
  20.      
  21.     public HelloItemizedOverlay(Drawable defaultMarker, Context context) { 
  22.         super(boundCenterBottom(defaultMarker)); 
  23.         mContext = context; 
  24.  
  25.         //fix the NullPointerException bug 
  26.         populate(); 
  27.     } 
  28.  
  29.     @Override 
  30.     protected OverlayItem createItem(int i) { 
  31.         OverlayItem item = null
  32.         try { 
  33.             item = mOverlayItems.get(i); 
  34.         } catch (ArrayIndexOutOfBoundsException e) { 
  35.             e.printStackTrace(); 
  36.         } catch (IndexOutOfBoundsException e) { 
  37.             e.printStackTrace(); 
  38.         } catch (Exception e) { 
  39.             e.printStackTrace(); 
  40.         } 
  41.         return item; 
  42.     } 
  43.      
  44.     @Override 
  45.     public int size() { 
  46.         return mOverlayItems.size(); 
  47.     } 
  48.  
  49.     /** 
  50.      * add new overlay item into overlay items array 
  51.      *  
  52.      * @param overlay 
  53.      */ 
  54.     public void addOverlay(OverlayItem overlay) { 
  55.         try { 
  56.             mOverlayItems.add(overlay); 
  57.              
  58.             //主动设置lastFocusedIndex = -1 
  59.             setLastFocusedIndex(-1); 
  60.             populate(); 
  61.         } catch (Exception e) { 
  62.             e.printStackTrace(); 
  63.         } 
  64.     } 
  65.      
  66.     /** 
  67.      * remove overlay item 
  68.      */ 
  69.     public void remove(OverlayItem overlayItem) { 
  70.         try { 
  71.             mOverlayItems.remove(overlayItem); 
  72.              
  73.             //主动设置lastFocusedIndex = -1 
  74.             setLastFocusedIndex(-1); 
  75.             populate(); 
  76.         } catch (Exception e) { 
  77.             e.printStackTrace(); 
  78.         } 
  79.     } 
  80.      
  81.     /** 
  82.      * clear overlays 
  83.      */ 
  84.     public void clear() { 
  85.         try { 
  86.             mOverlayItems.clear(); 
  87.              
  88.             //主动设置lastFocusedIndex = -1 
  89.             setLastFocusedIndex(-1); 
  90.             populate(); 
  91.         } catch (Exception e) { 
  92.             e.printStackTrace(); 
  93.         } 
  94.     } 

    至此,bill已将自己在Android Google Map API的ItemizedOverlay中遇到的陷阱及其解决方法介绍完毕,现在便可以安全地使用HelloItemizedOverlay类进行接下来的开发了。