在后台获取GoogleMap快照 [英] Get GoogleMap snapshot on background

查看:291
本文介绍了在后台获取GoogleMap快照的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图在后台获取GoogleMap(来自MapView)的快照,并用回调函数返回并在ImageView中显示它。在做这件事的时候,我试图让尽可能多的独立于布局的快照部分(所以,试图避免将MapView添加到布局中)。我试过的是以给定大小以编程方式创建一个MapView,添加一些路径和引脚,获取快照并返回它(所有订阅使用RxJava),但我得到以下错误,应用程序正在关闭,甚至没有
$ b


 致命例外:androidmapsapi-Snapshot过程:
com .package.name.debug,PID:2159
java.lang.IllegalArgumentException:宽度和高度必须>> 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)
,位于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?> {
map mapView = MapView(上下文)
mapView.layoutParams = ViewGroup.LayoutParams(specs.mapWidth,specs.mapHeight)

返回mapView.getMap()
.subscribeOn(AndroidSchedulers.mainThread())
.map {googleMap - >
setupMap(googleMap,mapData)//添加路径和引脚
googleMap
}
.flatMap {googleMap - >
googleMap.snapshot(mapData)
}
}

私人乐趣MapView.getMap():Single< GoogleMap> {
return Single.create {subscriber - >
$ b onStart()
onResume()
onCreate()$
onLt处理器=处理程序(Looper.getMainLooper())
handler.post {
onResume b
$ b getMapAsync {googleMap - >
subscriber.onSuccess(googleMap)
}
}
}
}

私人乐趣GoogleMap.snapshot(mapData:MapData):Single< ;位图> {
return Single.create {subscriber - >
snapshot {bitmap - >
saveMap(mapData.hashCode()。toString(),bitmap)
subscriber.onSuccess(bitmap)
}
}
}

在UI类中,我正在做
$ b $ pre> viewModel.mapProvider
.bitmapFor(map,viewModel.mapData,Specs(map.measuredWidth,map.measuredHeight))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this :: onMap,this :: onError)// onMap set的图片位图


解决方案

(请参阅更新!)似乎无法在后台隐藏 MapView ,因为当它不可见时,地图甚至没有开始加载。您可以为隐藏的 MapVew 设置正确的大小(例如,基于



如果您想在精简模式地图上显示我的位置点并使用默认位置源,需要调用 onResume() onPause()

  ... 
@Override
保护无效的onResume(){
super.onResume();
if(mMapView!= null){
mMapView.onResume();



@Override
保护无效onPause(){
if(mMapView!= null){
mMapView.onPause( );
}
super.onPause();
}
...

因为位置来源只会更新这些电话


I'm trying to get the snapshot of a GoogleMap (from MapView) on background, return it with a callback and show it in an ImageView. While doing this, I'm trying to make getting snapshot part independent from the layout as much as possible (so, trying to avoid adding a MapView to the layout). What I've tried was creating a MapView programmatically with given size, adding some paths and pins on it, getting the snapshot and returning it (all subscribed using RxJava) but I'm getting the following error and app is closing without even a "app stopped working" dialog.

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)

Here is the code for getting snapshot

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)
        }
    }
}

And in the UI class, I'm doing

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

解决方案

(Please see update!) Seems it impossible to get Google Map snapshot on background on invisible MapView because map didn't even starting to load, when it is invisible. You can set correct size for invisible MapVew (e.g. based on this article), but seems it will be empty black bar with "Google" logo. Possible simplest workaround - still show MapView, but disable controls and gestures on it:

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

    ...

}

or download map image via Static Map API and draw whatever you need locally on downloaded bitmap.

UPDATE:

It's possible to get Google Map snapshot on background on invisible MapView via setting it to Lite Mode on creation:

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

And it's necessary to determine the MapView size in onMapReady() via measure() and layout() calls to initiate map image loading - than, after map image loaded, it's possible get it in onMapLoaded() callback:

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);

It is mandatory to call onCreate() on MapView object before getMapAsyhc(), otherwise no map will appear.

Full source code:

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);
        }
    }
}

and 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>

which get screenshot with marker on map:

If you wish to show the My Location dot on your lite mode map and use the default location source, you will need to call onResume() and onPause():

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

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

because the location source will only update between these calls.

这篇关于在后台获取GoogleMap快照的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆