1

我有一个用于在我的MapView. 因为在draw我绘制路径的每一段的方法中,这很容易成为一个效率问题。

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

draw现在,该方法的参数之一是Canvas绘图。我知道如何在上面绘制缓存的位图,问题是我不知道如何在位图上缓存画布的内容。
我不能实例化一个新的画布,也不能调用setBitmap,因为画布在 a 中,如果调用该方法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);
}
4

1 回答 1

3

您无法将位图显示到Mapview画布正在绘制的位置。

方法应该如下:

  • 首先,您创建自己的(空的和透明的)位图,与MapView画布大小相同
  • 然后为您的位图创建您赢得的画布(此画布是您用来绘制位图的绘图工具)并使用它绘制路径。
  • 最后,将位图(已绘制路径)绘制到MapView画布上。

但是,您所指的性能/效率问题可能是由于您现有解决方案的设计不正确。在中型设备中,我可以在大约 3 毫秒内绘制具有 10.000 个点的路径,而无需使用位图(并且有一些不使用它们的充分理由)。

在我对这篇文章的回答中,有一些关于如何处理它的提示:Overlay.draw() calls many times。还要检查@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); 
}

路径构建

每次缩放更改时都必须构建路径。缩放变化检测是使用pathInitialLonSpanas getZoomLevel()is not syncronous 与地图缩放动画完成的。

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、pPrev 等)是在类级别定义的,以避免在每次运行方法时创建新对象。

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

问候。

于 2012-11-26T00:13:08.430 回答