Search code examples
androidgoogle-mapssetfocusitemizedoverlay

Android mapView ItemizedOverlay setFocus does not work properly


Calling setFocus(null) on the ItemizedOverlay does not 'unfocus' current marker. According to the documentation:

... If the Item is not found, this is a no-op. You can also pass null to remove focus.

Here's my code:

MapItemizedOverlay

public class MapItemizedOverlay extends ItemizedOverlay<OverlayItem> {
    private ArrayList<OverlayItem> items = new ArrayList<OverlayItem>();

    public MapItemizedOverlay(Drawable defaultMarker) {
        super(defaultMarker);
    }

    public void addOverlay(OverlayItem overlay) {
        items.add(overlay);
        populate();
    }

    @Override
    protected OverlayItem createItem(int i) {
        return items.get(i);
    }

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

}

Creating map overlay and one marker:

StateListDrawable youIcon = (StateListDrawable)getResources().getDrawable(R.drawable.marker_icon);
int width = youIcon.getIntrinsicWidth();
int height = youIcon.getIntrinsicHeight();
youIcon.setBounds(-13, 0-height, -13+width, 0);
GeoPoint location = new GeoPoint(40800816,-74122009);

MapItemizedOverlay overlay = new MapItemizedOverlay(youIcon);
OverlayItem item = new OverlayItem(location, "title", "snippet");
overlay.addOverlay(item);
mapView.getOverlays().add(overlay);

The R.drawable.marker_icon is defined as follows:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_focused="true" android:drawable="@drawable/marker_selected" />
    <item android:state_selected="true" android:drawable="@drawable/marker_selected" />
    <item android:drawable="@drawable/marker_normal" />
</selector>

Now, to test the setFocus() behavior I put the button on the activity window, with the following onClick listener:

Button focusBtn = (Button)findViewById(R.id.focusbtn);
focusBtn.setOnClickListener(new OnClickListener() {             
    @Override
    public void onClick(View v) {
        for(Overlay ov : mapView.getOverlays())
        {
            if(ov.getClass().getSimpleName().equals("MapItemizedOverlay") == true)
            {
                MapItemizedOverlay miv = (MapItemizedOverlay)ov;
                if(miv.getFocus() == null)
                    miv.setFocus(miv.getItem(0));
                else
                    miv.setFocus(null);
                break;
            }
        }
        mapView.invalidate();
    }
});

The expected behavior is: clicking on the button toggles marker selection.

It works only once - clicking it for the first time selects the marker, clicking it again does not de-select the marker. The most weird thing about it is that after calling setFocus(null), getFocus() also returns null - like the overlay has no focused item (I debugged it). But even after calling mapView.invalidate() the marker is still drawn in 'selected'(focused) state.


Solution

  • As Rpond said in a comment to my question, it looks like an open bug in the API.

    In the meantime I solved it myself. Below is the workaround code. You need to extend the OverlayItem class and check what overlay.getFocus() is returning.

    public class MapOverlayItem extends OverlayItem {
    
        MapItemizedOverlay overlay = null;
    
        public MapOverlayItem(GeoPoint point, MapItemizedOverlay ov)
        {
            super(point, null, null);
            this.overlay = ov;
        }
    
        public MapOverlayItem(GeoPoint point, String title, String snippet) {
            super(point, title, snippet);
        }
    
        @Override
        public Drawable getMarker(int stateBitset) {
            Drawable icon = overlay.getDefaultMarker();
    
            if(stateBitset == 0)
                return icon;
    
            OverlayItem focusedItem = overlay.getFocus();
    
            if(focusedItem == null) {
                OverlayItem.setState(icon, 0);
                return icon;
            }
    
            if(focusedItem.equals(this) == true)
                OverlayItem.setState(icon, stateBitset);
            else
                OverlayItem.setState(icon, 0);
    
            return icon;        
        }
    }