Android MapView叠加,缓存绘制位图

我在我的MapView上绘制路径的覆盖图,但我注意到它每秒钟不必要地重绘10次。 由于在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在同一篇文章中的答案。

    --EDITED--

    只是通过查看代码,我无法弄清楚为什么你会得到这个警告......但是让它变得复杂得多。

    按以下方式组织您的代码:

    draw()方法仅检查是否存在缩放更改(如果要求重建路径)以及map是否已移动(如果是这样的偏移路径)并最终绘制路径。

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

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

    问候。

    链接地址: http://www.djcxy.com/p/68571.html

    上一篇: Android MapView overlay, caching bitmap on draw

    下一篇: how to use two overlay in android map