Question

I have cleaned my code to have less lines as possible but shows the problems i encountered.

My map display some Marker depends on camera position.Marker are add on map by addMarker and stock in an HashMap where Long is to get reference between marker and a database record.

Each marker on the map contains a title and a snippet, that allows me to display InfoWindow. On map I had a onInfoWindowClickListener(Marker mark); that allow me to display a dialog contains more information to the user than the infoWindow can do. The Marker mark in params of the listener is used with the HashMap to retrieve the id of the database record to pass it to the dialog to retrieve information in the dialog.

The marker may be destroyed when the user moves on the screen by detecting if it was always on the screen or outside after the move. If it's not, I remove the marker from the HashMap and use the Marker.remove(); method to remove it from the map.

All of this works pretty well if you get lucky. Why? because I've found that Maps seems to reuse marker that was removed (why not) but the addMarker method and the mark pass to onInfoWindowClickListener are not the same in some case. comparing reference not work and equals not work too. So i can't compare using this two methods to compare marker in the HashMap and in parmas. Compare manually seems to be the only solution but before to do this i prefer to ask you if you have any idea on what's going on.

Have I made a mistake somewhere in the code below or it's a bug of Maps API that return a new marker but using an old? Is really an old marker or it was a sync problems between thread in the api? Is it a bad idea to make it as it, is there a better solution?

I don't know, maybe you have any idea?

public class MainActivity extends FragmentActivity{

    private enum CORNER {
            TOP_LEFT,
            TOP_RIGHT,
            BOT_LEFT,
            BOT_RIGHT
    };

    private GoogleMap map;
    private SupportMapFragment mapFrag;
    private Map<Long,Marker> markerMap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            setContentView(R.layout.main_activity);
            markerMap = new HashMap<Long, Marker>();    
            mapFrag = (SupportMapFragment)getSupportFragmentManager().findFragme   ntById(R.id.maps_mv_map);
            map = mapFrag.getMap();

            map.setOnCameraChangeListener(new OnCameraChangeListener() {

                    @Override
                    public void onCameraChange(CameraPosition position) {
                            addSiteOnMap();//update displayed marker
                    }
            });

            map.setOnInfoWindowClickListener(new OnInfoWindowClickListener() {

                    @Override
                    public void onInfoWindowClick(Marker arg0) {
                            InformationDialog dialog = new InformationDialog();
                            Bundle args = new Bundle();
                            System.out.println(arg0);//show reference to the marker to find the problem
                            //retrieve the db Id depends on marker
                            for(Long key : markerMap.keySet())
                            {
                                    if(markerMap.get(key).equals(arg0))
                                    {
                                            args.putLong("dbId",key);
                                            break;
                                    }
                            }
                            dialog.setArguments(args);
                            dialog.show(getSupportFragmentManager(), "information dialog");
                    }
            });
    }

    //get the LatLng for a corner
    private LatLng getCorner(CORNER corner)
    {
            int height = 0;
            int width = 0;
            if(corner == CORNER.BOT_LEFT || corner == CORNER.BOT_RIGHT)
                    height = mapFrag.getView().getHeight();
            if(corner == CORNER.BOT_RIGHT || corner == CORNER.TOP_RIGHT)
                    width = mapFrag.getView().getWidth();

            com.google.android.gms.maps.Projection pr = map.getProjection();
            return pr.fromScreenLocation(new Point(width,height));
    }

    private void addSitesOnMap()
    {
            List<Data> list = Database.getInstance(this).getData(getCorner(CORNER.TOP_RIGHT),getCorner(CORNER.BOT_LEFT));
            Set<Long> keys = markerMap.keySet();
            List<Long> idList = new ArrayList<Long>();
            for(Data site : list)
            {
                    idList.add(site.getDbId());
            }
            keys.retainAll(idList);
            Set<Long> oldKeys = markerMap.keySet();
            for(Long id : oldKeys)
            {
                    if(!keys.contains(id))
                    {
                            Marker marker = markerMap.remove(id);
                            marker.remove();
                    }
            }

            for(Data site : list)                          
            {
                    if(!markerMap.containsKey(site.getDbId()))//if the marker is not already display on the map
                    {
                            MarkerOptions options = new MarkerOptions();
                            options.position(site.getLoc());
                            options.title(site.getCityName());
                            DateFormat df = DateFormat.getDateInstance(DateFormat.FULL, Locale.getDefault());
                            options.snippet(df.format(site.getDate().getTime()));

                            Bitmap bp = BitmapFactory.decodeResource(getResources(), R.drawable.marker);
                            options.icon(BitmapDescriptorFactory.fromBitmap(bp));
                            markerMap.put(Long.valueOf(site.getDbId()),map.addMarker(options));
                    }
            }
            for(Long key : markerMap.keySet())
            {
                    System.out.println(markerMap.get(key).getTitle()+" "+key+" "+markerLi  st.get(key));//show reference to the all marker in the map and information to help me find what reference is interesting for me
            }
    }
}   

Thanks for your help

Was it helpful?

Solution

According to the Java documentation : keySet() "Returns a Set view of the keys contained in this map. The set is backed by the map, so changes to the map are reflected in the set, and vice-versa." (source)

The issue is in the following piece of code :

        List<Data> list = Database.getInstance(this).getData(getCorner(CORNER.TOP_RIGHT),getCorner(CORNER.BOT_LEFT));
        Set<Long> keys = markerMap.keySet();
        List<Long> idList = new ArrayList<Long>();
        for(Data site : list) {
                idList.add(site.getDbId());
        }
        // Here, you drop all ids that are not visible anymore
        // but before you had a chance to remove the marker
        keys.retainAll(idList);

You may want to do a copy of the keySet before fiddling with it. What you called oldKeys are actually the newKeys.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top