martes, 8 de septiembre de 2015

Android: Google maps, too many markers, avoid addding marker latency

MapFragment
public class MapFragment extends BaseFragment {

    private GoogleMap mMap;
    private CameraPosition previousCameraPosition;
    private HashMap<Marker, Facility> markerToData;
    private Facility selectedFacility;

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

        markerToData = new HashMap<>();
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.activity_maps, null);
    }

    @Override
    public void onResume() {
        super.onResume();
        setUpMapIfNeeded();
    }

    private void setUpMapIfNeeded() {

        if (mMap == null) {
            mMap = ((SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.map)).getMap();
            mMap.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() {

                @Override
                public void onCameraChange(CameraPosition newCameraPosition) {
                    if (differentCameraPosition(newCameraPosition)) {
                        previousCameraPosition = newCameraPosition;
                        showMarkers();
                    }
                }
            });
            mMap.setInfoWindowAdapter(new FacilityInfoWindow());
            mMap.setOnMyLocationChangeListener(new GoogleMap.OnMyLocationChangeListener() {
                @Override
                public void onMyLocationChange(Location location) {

                    CameraUpdate center = CameraUpdateFactory.newLatLng(new LatLng(location.getLatitude(), location.getLongitude()));
                    CameraUpdate zoom = CameraUpdateFactory.zoomTo(11);
                    mMap.moveCamera(center);
                    mMap.animateCamera(zoom);
                }
            });
            mMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
                @Override
                public boolean onMarkerClick(Marker marker) {
                    selectedFacility = markerToData.get(marker);

                    return false;
                }
            });
        }
    }

    private boolean differentCameraPosition(CameraPosition newCameraPosition) {

        if (previousCameraPosition == null) {
            return true;
        }

        boolean differentZoom =  Float.floatToIntBits(previousCameraPosition.zoom) != Float.floatToIntBits(newCameraPosition.zoom);
        boolean differentLatitude =  Double.doubleToLongBits(previousCameraPosition.target.latitude) != Double.doubleToLongBits(newCameraPosition.target.latitude);
        boolean differentLongitude =  Double.doubleToLongBits(previousCameraPosition.target.longitude) != Double.doubleToLongBits(newCameraPosition.target.longitude);

        return differentZoom || differentLatitude || differentLongitude;
    }

    private void showMarkers() {

        if (mMap != null) {
            LatLngBounds bounds = mMap.getProjection().getVisibleRegion().latLngBounds;

            LatLng northeast = bounds.northeast;
            LatLng southwest = bounds.southwest;

            mMap.clear();

            List<Facility> facilities = Cache.getFacilitiesInBounds(northeast, southwest);

            BitmapDescriptor defaultMarker = BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_ORANGE);

            for (Facility facility : facilities) {

                Marker marker = mMap.addMarker(new MarkerOptions()
                        .position(new LatLng(facility.getLatitude(), facility.getLongitude()))
                        .draggable(false)
                        .visible(true)
                        .icon(defaultMarker)
                        .title(facility.getName()));

                markerToData.put(marker, facility);

                if (selectedFacility!= null && selectedFacility.getFacilityId() == facility.getFacilityId()) {
                    marker.showInfoWindow();
                    selectedFacility = null;
                }
            }
        }
    }

    private class FacilityInfoWindow implements GoogleMap.InfoWindowAdapter {

        @Override
        public View getInfoWindow(Marker marker) {

            Facility facility = markerToData.get(marker);

            View view = LayoutInflater.from(getActivity()).inflate(R.layout.facility_info_window, null);
            ((TextView)view.findViewById(R.id.title)).setText(facility.getName());
            ((TextView)view.findViewById(R.id.subtitle)).setText(facility.getAddress());

            return view;
        }

        @Override
        public View getInfoContents(Marker marker) {
            return null;
        }
    }
}
Important:
"differentCameraPosition" is necesary since "onCameraChange" will be called every time you click on a marker even if you are already centered on it, resulting in the info window to disappear a second later after clicking on the marker. Also use "selectedFacility" to avoid the info window from disappearing when centering the map around the marker.

No hay comentarios:

Publicar un comentario