Android MapView覆盖,绘制时缓存位图



我有一个用于在MapView上绘制路径的覆盖,但我注意到它每秒会被不必要地重新绘制十次。由于在draw方法中,我绘制路径的每一段,这很容易成为效率问题。

出于这个原因,我决定缓存覆盖的内容,并在必要时重新绘制,即路径发生变化、地图中心发生移动或缩放级别发生变化时。

现在,draw方法的参数之一是要绘制的Canvas。我知道如何在上面绘制缓存的位图,问题是我不知道如何在位图上缓存画布的内容
我不能实例化新的画布,也不能调用setBitmap,因为画布在HardwareCanvas中,如果调用该方法,它将抛出UnsupportedOperationException

所以,概括一下,我有一个画布和一个位图,我如何将画布的内容复制到位图?

编辑
为了清晰起见,这是我的绘制方法,我不会手动调用它,但即使地图根本没有移动,它仍然会被反复调用

public void draw(Canvas canvas, MapView map, boolean shadow) {
if (shadow) {  
// this overlay doesn't need to draw shadows  
return;
}
if (paths.isEmpty()) {
// nothing to draw
return;
}
center = map.getMapCenter();
zoomLevel = map.getZoomLevel();
map.getDrawingRect(bounds);
projection = map.getProjection();
maxLevel = map.getMaxZoomLevel();
for (MapPath mp : paths) {
// adjust path width to current zoom
adjustedWidth = mp.getWidth() * zoomLevel / maxLevel;
if (adjustedWidth < MIN_WIDTH_TO_DRAW) {
// path is too thin, avoid drawing it
continue;
}
paint.setStrokeWidth(adjustedWidth);
paint.setColor(mp.getColor());
state = PathState.FIRST_POINT;
path.reset();
for (PathPoint pp : mp.getPoints()) {
if (!pp.shoudAppearAtZoomLevel(zoomLevel)) {
// do not draw this point at this zoom level
continue;
}
// project a geopoint to a pixel
projection.toPixels(pp.getGeoPoint(), point);
inside = isInsideBounds(point, map);
switch (state) {
case FIRST_POINT:
// move to starting point
firstX = point.x;
firstY = point.y;
path.moveTo(firstX, firstY);
break;
case WAS_INSIDE:
// segment is completely or partially on map
path.lineTo(point.x, point.y);
break;
case WAS_OUTSIDE:
if (inside) {
// segment is partially on map
path.lineTo(point.x, point.y);
} else {
// segment is completely off map
path.moveTo(point.x, point.y);
}
break;
}
// update state
state = inside ? PathState.WAS_INSIDE : PathState.WAS_OUTSIDE;
}
// workaround to avoid canvas becoming too big when path is mostly off screen
path.moveTo(firstX, firstY);
// draw this path to canvas
canvas.drawPath(path, paint);
}
super.draw(canvas, map, shadow);
}

无法将位图绘制到Mapview画布所在的位置。

方法应如下:

  • 首先创建自己的(空且透明)位图,大小与MapView画布相同
  • 然后为位图创建获胜画布(此画布是用于绘制位图的绘图工具),并使用它绘制路径
  • 最后,在MapView画布上绘制位图(已绘制路径)

但是,您提到的性能/效率问题可能是由于现有解决方案的设计不正确造成的。在中等范围的设备中,我可以在大约3ms内绘制具有10.000个点的路径,而无需使用位图(有一些很好的理由不使用它们)。

在我对这篇文章的回答中,有一些关于如何处理它的提示:Overlay.draw()调用了很多次。还可以查看@shkschneider在同一篇文章中的回答。

--已编辑--

仅仅通过查看代码,我就不明白你为什么会收到这个警告。。。但正在使它变得比它需要的复杂得多。

按照以下方式组织代码:

绘制

draw()方法仅检查是否有缩放更改(如果有,则要求重建路径)以及地图是否已移动(如果有偏移路径),并最终绘制路径。

@Override
public void draw(Canvas canvas, MapView mapview, boolean shadow) {
super.draw(canvas, mapview, shadow);
if(shadow) return;
if(mp.getPoints() == null || mp.getPoints().size() < 2) return;
Projection projection = mapview.getProjection();
int lonSpanNew = projection.fromPixels(0,mapview.getHeight()/2).getLongitudeE6() - 
projection.fromPixels(mapview.getWidth(),mapview.getHeight()/2).getLongitudeE6();
if(lonSpanNew != pathInitialLonSpan)
pathBuild();
else{ //check if path need to be offset
projection.toPixels(mp.getPoints().get(0), p1);
if(p1.x != pathInitialPoint.x || p1.y != pathInitialPoint.y){
path.offset(p1.x - pathInitialPoint.x, p1.y - pathInitialPoint.y);
pathInitialPoint.x = p1.x;
pathInitialPoint.y = p1.y;
}
}
canvas.drawPath(path, paint); 
}

pathBuild

每次缩放更改时都必须构建路径。使用pathInitialLonSpan进行缩放变化检测,因为getZoomLevel()与地图缩放动画不同步。

private void pathBuild(){
path.rewind(); 
if(mp.getPoints() == null || mp.getPoints().size() < 2) return;
Projection projection = mapView.getProjection();
pathInitialLonSpan = projection.fromPixels(0,mapView.getHeight()/2).getLongitudeE6() - 
projection.fromPixels(mapView.getWidth(),mapView.getHeight()/2).getLongitudeE6();
projection.toPixels(mp.getPoints().get(0), pathInitialPoint);
path.moveTo(pathInitialPoint.x,pathInitialPoint.y); 
for(int i=1; i<mp.getPoints().size(); i++){
projection.toPixels(mp.getPoints().get(i), p1);
int distance2 = (pPrev.x - p1.x) * (pPrev.x - p1.x) + (pPrev.y - p1.y) * (pPrev.y - p1.y); 
if(distance2 > 9){
path.lineTo(p1.x,p1.y);
pPrev.set(p1.x, p1.y);
}
}

一些对象(如p1、pPrevi等)是在类级别定义的,以避免每次方法运行时都创建新的对象。

Note:我已经更改了变量名以适应您正在使用的名称。我希望我没有犯任何错误,但你应该能够弄清楚。

谨致问候。