1

にパスを描画するためのオーバーレイがありますが、MapView毎秒約 10 回不必要に再描画されることに気付きました。このdraw方法では、パスのすべてのセグメントを描画するため、これは簡単に効率の問題になる可能性があります。

このため、オーバーレイのコンテンツをキャッシュし、実際に必要なときに再描画することにしました。つまり、パスが変更されたとき、マップの中心が移動したとき、またはズーム レベルが変更されたときです。

drawここで、メソッドのパラメーターの 1 つはCanvasto draw on です。キャッシュされたビットマップを描画する方法は知っていますが、キャンバスのコンテンツをビットマップにキャッシュする方法がわからないという問題があります。
新しいキャンバスをインスタンス化できません。また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() は何度も呼び出されます。同じ投稿で@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); 
}

パスビルド

パスは、ズームが変更されるたびに構築する必要があります。ズーム変更の検出は、マップ ズーム アニメーションと同期していないため、使用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:使用している変数名に合わせて変数名を変更しました。私は間違いを犯していないことを願っていますが、あなたはそれを理解できるはずです.

よろしく。

于 2012-11-26T00:13:08.430 に答える