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

Android MapView覆盖,绘制时缓存位图,android,caching,overlay,android-mapview,android-canvas,Android,Caching,Overlay,Android Mapview,Android Canvas,我有一个覆盖图,可以在我的地图视图上绘制路径,但我注意到它每秒被不必要地重画十次。 由于在draw方法中,我绘制了路径的每一段,因此这很容易成为一个效率问题 出于这个原因,我决定缓存覆盖的内容,并在必要时(即路径更改、地图中心移动或缩放级别更改时)实际重新绘制它 现在,draw方法的参数之一是要绘制的Canvas。我知道如何在上面绘制缓存的位图,问题是我不知道如何在位图上缓存画布的内容。 我不能实例化一个新画布,也不能调用setBitmap,因为hardwareCavas中的画布会抛出一个Uns

我有一个覆盖图,可以在我的
地图视图上绘制路径,但我注意到它每秒被不必要地重画十次。
由于在
draw
方法中,我绘制了路径的每一段,因此这很容易成为一个效率问题

出于这个原因,我决定缓存覆盖的内容,并在必要时(即路径更改、地图中心移动或缩放级别更改时)实际重新绘制它

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

总而言之,我有一个画布和一个位图,如何将画布的内容复制到位图

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

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);
}
public void draw(画布、地图视图、布尔阴影){
如果(阴影){
//此覆盖不需要绘制阴影
返回;
}
if(path.isEmpty()){
//没什么可画的
返回;
}
center=map.getMapCenter();
zoomLevel=map.getZoomLevel();
map.getDrawingRect(边界);
projection=map.getProjection();
maxLevel=map.getMaxZoomLevel();
用于(映射路径mp:路径){
//将路径宽度调整为当前缩放
adjustedWidth=mp.getWidth()*zoomLevel/maxLevel;
如果(调整宽度<最小宽度到绘图){
//路径太细,请避免绘制它
继续;
}
油漆。设置行程宽度(调整宽度);
paint.setColor(mp.getColor());
state=PathState.FIRST_点;
path.reset();
for(路径点pp:mp.getPoints()){
如果(!pp.shoudAppearAtZoomLevel(zoomLevel)){
//不要在此缩放级别绘制此点
继续;
}
//将地质点投影到像素
toPixels(pp.getGeoPoint(),point);
内部=isInsideBounds(点、地图);
开关(状态){
案例第一点:
//移到起点
firstX=点x;
firstY=点y;
path.moveTo(firstX,firstY);
打破
案情如下:
//段完全或部分在地图上
lineTo路径(点x、点y);
打破
案件发生在:
如果(内部){
//部分路段在地图上
lineTo路径(点x、点y);
}否则{
//这个片段完全偏离了地图
路径移动到(点x,点y);
}
打破
}
//更新状态
state=内部?PathState.WAS\u内部:PathState.WAS\u外部;
}
//解决方法,以避免画布在路径大部分在屏幕外时变得过大
path.moveTo(firstX,firstY);
//将此路径绘制到画布
画布.绘制路径(路径,绘制);
}
超级。绘制(画布、地图、阴影);
}

无法将位图移到绘制
Mapview
画布的位置

方法应如下:

  • 首先,创建自己的(空且透明)位图,其大小与
    MapView
    画布相同
  • 然后为位图创建WIN画布(此画布是用于绘制位图的绘图工具),并使用它绘制路径
  • 最后,将位图(已绘制路径)绘制到
    MapView
    画布
但是,您提到的性能/效率问题可能是由于现有解决方案的设计不正确造成的。在中等范围的设备中,我可以在大约3毫秒的时间内,不使用位图(并且有一些不使用位图的好理由)绘制具有10.000个点的路径

在我对这篇文章的回答中,有一些关于如何处理它的提示:。也可以在同一帖子中查看@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); 
}
某些对象(即p1、pPrev等)在类级别定义,以避免每次Methodos运行时创建新对象

注意:
我已经更改了变量名,以适合您正在使用的变量名。我希望我没有犯任何错误,但你应该能找到答案


尊敬。

我的地图没有任何慢下来的问题,我非常确定我在解决方案的设计方面做得很好(我遵循了许多最佳实践,还包括了一些优化)只是我认为缓存结果路径比无限每秒重新计算十次更有效。。。但是,您是否建议避免使用位图?我可以问你为什么吗?我建议你重新检查最佳实践。。。首先,仅当地图移动/缩放或根据您的请求(不是每秒10次)时才绘制覆盖图,其次,您可以将路线缓存在
路径上
(虽然绘制时比位图慢一点,但它使用的内存要少很多,并且需要在缩放更改时重新计算,而不是在地图移动时)。使用位图的缺点是:1-它们需要
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);
        }
    }