1

ビットマップからマップマーカーを作成する正しい方法は何でしょうか?

私はGoogleMapsAndroid v1を使用していて、現在v2に移行しています。
私の地図には、ビットマップから作成された複数のマーカーが含まれています。
各マーカーは、ビットマップイメージと、マーカーごとに異なる可能性のあるテキストで構成されています。したがって、メモリ内の各ビットマップを変更する必要があります。
次のようなマーカー画像を読み込みます。

private Bitmap getMarker(final String name) {
    Bitmap result = BitmapFactory.decodeFile(context.getFilesDir() + "/markers/" + name + ".png");

    if (result == null) {
        // must make a mutable copy as by default resource is returned immutable
        result = decodeResource(context.getResources(), R.drawable.default_marker).copy(Config.ARGB_8888, true);
    } else {
        result = result.copy(Config.ARGB_8888, true);
    }

    return result;
}

次に、キャンバスを使用してカスタムテキストを適用します。

private void applyText(Bitmap marker, Paint paint, String text) {
    Canvas canvas = new Canvas(marker);
    canvas.drawText(text, calculateTextX(marker, paint, text), calculateTextY(marker, paint, text), paint);
}

マーカー画像は、MDPIデバイス上の最大5KBサイズのマルチカラーPNGファイルです。

Google Maps Android v1一部のデバイス(再現が難しい)ではjava.lang.OutOfMemoryError、ビットマップイメージのデコード中に時々取得しました。そして、私は何か間違ったことをしているような気がします...そして私は私がGoogle Maps Android v2同じ問題を起こさないことを確かに知りたいです。

4

1 に答える 1

2

数日前にv2に切り替えたときに同じ問題が発生しました。最も重要なことは、イメージをメモリにできるだけロードしないようにすることです。したがって、必要な限り、マーカーの元のビットマップをメモリに保持します。

これは、私がそれをどのように行ったかを示すコードのサンプルです。

public class MyMarkerFactoryFactory {

    private Context ctx;
    private Bitmap cachedMarkerBase; // Cached bitmap
    private Bitmap currentMarker; // Working copy
    private final List<Marker> markers = new ArrayList<Marker>();

    public MyMarkerFactoryFactory(Context ctx, String markerName, int markerWidth, int markerHeight) {
        this.ctx = ctx;

        Bitmap src = BitmapFactory.decodeFile(ctx.getFilesDir() + "/markers/" + markerName + ".png");
        int srcBitmapWidth = src.getWidth();
        int srcBitmapHeight = src.getHeight();
        if (markerWidth != srcBitmapWidth && markerHeight != srcBitmapHeight) {
            // The rendering bitmap will depend on provided width and height
            // In my case it's because the bitmap does not have the same pixel size
            // depending on the display pixel density. So I've declared the size I
            // I want in "dp" somewhere else and fetch it from ctx.getDimen
            // createScaledBitmap will return the same bitmap if you are scaling
            // to the same size, so it's good to test the size before you rescale
            // otherwise you are likely to recycle() the bitmap you wanted to use.
            cachedMarkerBase = Bitmap.createScaledBitmap(src, markerWidth, markerHeight, false);
            src.recycle();
        } else {
            cachedMarkerBase = src;
        }
        currentMarker = cachedMarkerBase.copy(cachedMarkerBase.getConfig(), true);
    }

    public void onDestroy() {
        clearOverlays();
        if (cachedMarkerBase != null) {
            cachedMarkerBase.recycle();
            cachedMarkerBase = null;
        }
        if (currentMarker != null) {
            currentMarker.recycle();
            currentMarker = null;
        }
    }

    public void clearOverlays() {
        for (Marker marker : markers) {
            marker.remove();
        }
        markers.clear();
    }

    public void addOverlay(GoogleMap map, LatLng position, String myText) {
        drawMarkerWith(myText);
        BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(currentMarker);
        markers.add(map.addMarker(new MarkerOptions().anchor(0.5f, 0.5f).icon(bitmapDescriptor).position(position)));
    }

    private void drawMarkerWith(String myText) {
        // Copy image from cache
        for (int i = 0; i < cachedMarkerBase.getWidth(); i++) {
            for (int j = 0; j < cachedMarkerBase.getHeight(); j++) {
                currentMarker.setPixel(i, j, cachedMarkerBase.getPixel(i, j));
            }
        }
        int x = currentMarker.getWidth() / 2, y = currentMarker.getHeight() / 2;

        Paint paintForText = new Paint()
        paintForText.setTextSize(7f); // TODO

        // Draw text
        Canvas canvas = new Canvas(currentMarker);
        int x = 0; // TODO
        int y = 0; // TODO
        canvas.drawText(myText, x, y, paintForText);
    }
}

もちろん、異なるマーカーの数が限られている場合、これはうまく機能します。アクティビティの開始時にファクトリを作成し、停止時に破棄するだけで、メモリには 2 つのビットマップしかなく、大量のビットマップのロード/リリースを回避できます。

于 2013-02-24T09:24:43.967 に答える