在后台获取GoogleMap快照

时间:2018-02-05 11:17:10

标签: android google-maps rx-java android-mapview

我正在尝试在后台获取GoogleMap(来自MapView)的快照,通过回调返回它并在ImageView中显示它。在这样做时,我试图尽可能使快照部分独立于布局(因此,试图避免在布局中添加MapView)。我尝试过以编程方式创建一个给定大小的MapView,在其上添加一些路径和引脚,获取快照并返回它(所有使用RxJava订阅)但我收到以下错误,应用程序正在关闭,甚至没有“ app停止工作“对话框。

FATAL EXCEPTION: androidmapsapi-Snapshot Process:
com.package.name.debug, PID: 2159
    java.lang.IllegalArgumentException: width and height must be > 0
    at android.graphics.Bitmap.createBitmap(Bitmap.java:1013)
    at android.graphics.Bitmap.createBitmap(Bitmap.java:980)
    at android.graphics.Bitmap.createBitmap(Bitmap.java:930)
    at android.graphics.Bitmap.createBitmap(Bitmap.java:891)
    at com.google.maps.api.android.lib6.impl.ec.b(:com.google.android.gms.DynamiteModulesB@11975940:4)
    at com.google.maps.api.android.lib6.impl.bf.run(:com.google.android.gms.DynamiteModulesB@11975940:4)
    at java.lang.Thread.run(Thread.java:764)

以下是获取快照的代码

fun bitmapFor(mapData: MapData, specs: Specs): Single<Bitmap?> {
    val mapView = MapView(context)
    mapView.layoutParams = ViewGroup.LayoutParams(specs.mapWidth, specs.mapHeight)

    return mapView.getMap()
            .subscribeOn(AndroidSchedulers.mainThread())
            .map { googleMap ->
                setupMap(googleMap, mapData) // Adds paths and pins
                googleMap
            }
            .flatMap { googleMap ->
                googleMap.snapshot(mapData)
            }
}

private fun MapView.getMap(): Single<GoogleMap> {
    return Single.create { subscriber ->

        val handler = Handler(Looper.getMainLooper())
        handler.post {
            onCreate(null)
            onStart()
            onResume()

            getMapAsync { googleMap ->
                subscriber.onSuccess(googleMap)
            }
        }
    }
}

private fun GoogleMap.snapshot(mapData: MapData): Single<Bitmap> {
    return Single.create { subscriber ->
        snapshot { bitmap ->
            saveMap(mapData.hashCode().toString(), bitmap)
            subscriber.onSuccess(bitmap)
        }
    }
}

在UI类中,我正在做

viewModel.mapProvider
        .bitmapFor(map, viewModel.mapData, Specs(map.measuredWidth, map.measuredHeight))
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(this::onMap, this::onError) // onMap set's the image bitmap

1 个答案:

答案 0 :(得分:1)

请参阅更新!

似乎无法在隐身MapView的背景上获取Google Map快照,因为当地图不可见时,地图甚至没有开始加载。您可以为不可见MapVew设置正确的尺寸(例如,基于this文章),但似乎它将是空白黑条,其中包含&#34; Google&#34;商标。可能最简单的解决方法 - 仍显示MapView,但禁用其上的控件和手势:

`public void onMapReady(GoogleMap googleMap) {
    mGoogleMap = googleMap;
    mGoogleMap.getUiSettings().setAllGesturesEnabled(false);
    ...
}`

或通过Static Map API下载地图图像,并在下载的位图上本地绘制所需内容。

<强>更新

通过在创建时将Lite Mode设置为otherwise no map will appear,可以在不可见MapView的背景上获取Google地图快照:

...
GoogleMapOptions options = new GoogleMapOptions()
        .compassEnabled(false)
        .mapToolbarEnabled(false)
        .camera(CameraPosition.fromLatLngZoom(KYIV,15))
        .liteMode(true);
mMapView = new MapView(this, options);
...

MapView通过onMapReady()measure()调用启动地图图片加载时确定layout()尺寸是必要的,而不是在加载地图图片后,它可以在onMapLoaded()回调中获取:

mMapView.setDrawingCacheEnabled(true);
mMapView.measure(View.MeasureSpec.makeMeasureSpec(mMapWidth, View.MeasureSpec.EXACTLY),
        View.MeasureSpec.makeMeasureSpec(mMapHeight, View.MeasureSpec.EXACTLY));
mMapView.layout(0, 0, mMapWidth, mMapHeight);
mMapView.buildDrawingCache(true);
Bitmap b = Bitmap.createBitmap(mMapView.getDrawingCache());  // <- that is bitmap with map image
mMapView.setDrawingCacheEnabled(false);

必须在onCreate()MapView screenshot example之前致电MapView对象上的getMapAsyhc()

完整源代码:

public class MainActivity extends AppCompatActivity {

    private static final String MAP_VIEW_BUNDLE_KEY = "MapViewBundleKey";
    static final LatLng KYIV = new LatLng(50.450311, 30.523730);

    private ImageView mImageView;

    private MapView mMapView;
    // dimensions of map image
    private int mMapWidth = 600;
    private int mMapHeight = 800;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mImageView = (ImageView) findViewById(R.id.image_view);

        Bundle mapViewBundle = null;
        if (savedInstanceState != null) {
            mapViewBundle = savedInstanceState.getBundle(MAP_VIEW_BUNDLE_KEY);
        }

        GoogleMapOptions options = new GoogleMapOptions()
                .compassEnabled(false)
                .mapToolbarEnabled(false)
                .camera(CameraPosition.fromLatLngZoom(KYIV,15))
                .liteMode(true);
        mMapView = new MapView(this, options);
        mMapView.onCreate(mapViewBundle);

        mMapView.getMapAsync(new OnMapReadyCallback() {
            @Override
            public void onMapReady(GoogleMap googleMap) {

                // form your card (add markers, path etc.) here: 
                googleMap.addMarker(new MarkerOptions()
                        .position(KYIV)
                        .title("Kyiv"));

                // set map size in pixels and initiate image loading
                mMapView.measure(View.MeasureSpec.makeMeasureSpec(mMapWidth, View.MeasureSpec.EXACTLY),
                        View.MeasureSpec.makeMeasureSpec(mMapHeight, View.MeasureSpec.EXACTLY));
                mMapView.layout(0, 0, mMapWidth, mMapHeight);

                googleMap.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
                    @Override
                    public void onMapLoaded() {
                        mMapView.setDrawingCacheEnabled(true);
                        mMapView.measure(View.MeasureSpec.makeMeasureSpec(mMapWidth, View.MeasureSpec.EXACTLY),
                                View.MeasureSpec.makeMeasureSpec(mMapHeight, View.MeasureSpec.EXACTLY));
                        mMapView.layout(0, 0, mMapWidth, mMapHeight);
                        mMapView.buildDrawingCache(true);
                        Bitmap b = Bitmap.createBitmap(mMapView.getDrawingCache());
                        mMapView.setDrawingCacheEnabled(false);
                        mImageView.setImageBitmap(b);

                    }
                });

            }
        });
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        Bundle mapViewBundle = outState.getBundle(MAP_VIEW_BUNDLE_KEY);
        if (mapViewBundle == null) {
            mapViewBundle = new Bundle();
            outState.putBundle(MAP_VIEW_BUNDLE_KEY, mapViewBundle);
        }
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="{PACKAGE_NAME}.MainActivity">

    <ImageView
        android:id="@+id/image_view"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        />

</RelativeLayout>

在地图上用标记获取屏幕截图:

between these calls

如果您希望在精简模式地图上显示我的位置点并使用默认位置来源,则需要致电onResume()onPause()

...
@Override
protected void onResume() {
    super.onResume();
    if (mMapView != null) {
        mMapView.onResume();
    }
}

@Override
protected void onPause() {
    if (mMapView != null) {
        mMapView.onPause();
    }
    super.onPause();
}
...

因为位置来源只会更新How to add a named vector as a row to a data frame