¿Cómo almacenar en caché android.graphics.path o Bitmap cuando se usan Overlays?
Estoy usando una superposición para marcar áreas en Google Maps dibujando una forma de diez miles de GeoPoints que obtengo de cualquier fuente. Esto funciona y se ve así:
@Override public void draw(android.graphics.Canvas canvas, MapView mapView, boolean shadow) { super.draw(canvas, mapView, false); Projection projection = mapView.getProjection(); List<Zone> zones = ApplicationContext.getZones(); path.rewind(); for (Zone zone : zones) { paint.setDither(true); paint.setStyle(Style.FILL); paint.setAlpha(40); MultiPolygon multiPolygon = zone.getMultiPolygon(); List<Polygon> polygons = multiPolygon.getPolygons(); for (Polygon polygon : polygons) { for (List<Coordinate> coordinates : polygon.getCoordinates()) { for (int i = 0; i < coordinates.size(); i++) { Point p = new Point(); projection.toPixels(new GeoPoint((int)(coordinates.get(i).getLatitude() * 1E6), (int)(coordinates.get(i).getLongitude() * 1E6)), p); if (i == 0) { path.moveTo(px, py); } else { path.lineTo(px, py); } } } } } canvas.drawPath(path, paint); }
El problema es que esto es muy consumidor de recursos. Cada vez que se desplaza o mueve el mapa en MapView, la ruta tiene que ser calculada una y otra vez, porque se han cambiado las coordenadas de píxeles. El área dibujada podría llegar a ser tan grande que el desplazamiento en el MapView es tan lento que es funcional inutilizable.
- Dibujar texto en la parte superior de mapa de bits no
- Android: Cuando el teclado aparece, el diseño es invisible. ¿Cómo puedo solucionar esto?
- Las imágenes transparentes de OpenGL tienen negro en ellas
- Android Crear imagen "Ghost" (Touch-through)
- Mapas de Android NullPointerException ItemizedOverlay
Mis ideas son
- De alguna manera caché la "forma" que la ruta genera y sólo lo vuelve a dibujar cuando el nivel de zoom cambia en el MapView.
- Para dibujar de alguna manera la pintura en un "sobre la marcha" -Bitmap para utilizarlo como superposición (tal vez como ItemizedOverlay), escuchar MapView desplazamiento y mover el mapa de bits por la distancia desplazada.
No estoy seguro si hay mejores métodos.
¿Alguna idea de cómo podría solucionar este problema? (Estoy utilizando Google Maps API 1 y no puedo cambiar).
- Android: Si los marcadores se superponen entre sí en el mapa, el evento de clic se disparará para el último escondido
- Creación de una ventana de superposición del sistema (siempre en la parte superior)
- Mi mapa no muestra el mapa sólo muestra los elementos de superposición
- Diferentes marcadores con nombre en Google Android Map
- Superposición sobre un ImageView en Android
- El desplazamiento y el zoom de MapView es lento después de agregar muchas superposiciones
- Cómo forzar mezcla alfa en un videoview
- Servicio con Overlay - presione el botón de retroceso
Antes de recurrir a intentar averiguar cómo coincidir con el movimiento del mapa, hay algunas optimizaciones a su código actual que probablemente dará un ahorro significativo. En particular, estas dos líneas dentro de su bucle interno se ejecutan la mayoría de las veces, pero bastante costosas de ejecutar (dos asignaciones de memoria, punto flotante multiplica y cuatro llamadas de método).
Point p = new Point(); projection.toPixels(new GeoPoint((int)(coordinates.get(i).getLatitude() * 1E6), (int)(coordinates.get(i).getLongitude() * 1E6)), p);
En primer lugar, sólo necesitas un objeto Point
, así que evita asignarlo en tu bucle. Muévalo justo debajo de su path.rewind();
En segundo lugar, si pre-calculó sus coordenadas como GeoPoints
lugar de calcularlas cada vez, se ahorraría un montón de procesamiento en su rutina de draw
. También puede deshacerse de esa declaración if
con un poco de trabajo. Suponiendo que usted preconverte su lista de coordinate
a una lista de GeoPoint
, y la haga disponible a través de polygon.getGeoCoordinates()
, podría terminar con sus bucles internos que parecen –
for (List<GeoPoint> geoCoordinates : polygon.getGeoCoordinates()) { projection.toPixels(geoCoordinates.get(0),p); path.moveTo(px, py); // move to first spot final List<GeoPoint> lineToList = geoCoordinates.sublist(1,geoCoordinates.size()); // A list of all the other points for(GeoPoint gp : lineToList) { projection.toPixels(gp, p); path.lineTo(px, py); } }
Y eso funcionará mucho más rápido que lo que estabas haciendo antes.
Después de retocar en los últimos días encontré una posible solución (y no creo que haya una mejor) para no dibujar el camino una y otra vez, pero moverlo a la posición actual.
La parte difícil era averiguar cómo guardar en caché la forma dibujada para no calcularla una y otra vez. Esto se puede hacer usando una matriz. Con esta matriz (imagino esto como una especie de "plantilla") puede manipular las coordenadas de puntos dentro de la ruta. La primera vez (cuando alguien comienza a mover el mapa) dibujo el área como de costumbre. Cuando intenta calcularlo por segunda vez o más, no vuelvo a dibujar la forma pero manipulo el camino calculando el "delta" desde el punto actual hasta el último punto. Sé cuál es el punto actual, porque siempre mapeo el GeoPoint original (que siempre permanece igual) hasta el punto que resulta de la proyección actual. El "delta" necesita ser configurado como Matrix. Después de eso transformo el camino usando esta nueva matriz. El resultado es realmente muy rápido. El desplazamiento del mapa es tan rápido como si no se utilizara una superposición.
Esto se parece a esto (esto no es un código de producción, y no puede lidiar con el zoom todavía, pero muestra el principio que utilizo como base para mis optimizaciones):
public class DistrictOverlay extends Overlay { // private final static String TAG = DistrictOverlay.class.getSimpleName(); private Paint paint = new Paint(); private Path path = new Path(); private boolean alreadyDrawn = false; private GeoPoint origGeoPoint; Point p = new Point(); Point lastPoint = new Point(); @Override public void draw(android.graphics.Canvas canvas, MapView mapView, boolean shadow) { super.draw(canvas, mapView, false); Projection projection = mapView.getProjection(); List<Zone> zones = ApplicationContext.getZones(); if (!alreadyDrawn) { path.rewind(); for (Zone zone : zones) { if (!zone.getZoneId().equals(MenuContext.getChosenZoneId())) { continue; } String dateString = zone.getEffectiveFrom().trim().replace("CEST", "").replace("GMT", "").replace("CET", "").replace("MESZ", ""); if (DateUtil.isBeforeCurrentDate(dateString)) { paint.setColor(Color.RED); } else { paint.setColor(Color.GREEN); } paint.setDither(true); paint.setStyle(Style.FILL); paint.setAlpha(40); MultiPolygon multiPolygon = zone.getMultiPolygon(); List<Polygon> polygons = multiPolygon.getPolygons(); for (Polygon polygon : polygons) { for (List<GeoPoint> geoPoints : polygon.getGeoPoints()) { projection.toPixels(geoPoints.get(0), p); path.moveTo(px, py); origGeoPoint = new GeoPoint(geoPoints.get(0).getLatitudeE6(), geoPoints.get(0).getLongitudeE6()); lastPoint = new Point(px, py); final List<GeoPoint> pathAsList = geoPoints.subList(1, geoPoints.size()); for (GeoPoint geoPoint : pathAsList) { projection.toPixels(geoPoint, p); path.lineTo(px, py); } } } } } else { projection.toPixels(origGeoPoint, p); Matrix translateMatrix = new Matrix(); translateMatrix.setTranslate(px - lastPoint.x, py - lastPoint.y); path.transform(translateMatrix); lastPoint = new Point(px, py); } canvas.drawPath(path, paint); if (!path.isEmpty()) { alreadyDrawn = true; } } @Override public boolean onTap(GeoPoint p, MapView mapView) { return true; } }
- Android – trazado de gps coordinar en mapa personalizado
- Pasar datos de búsqueda a la actividad de búsqueda