添加边界以避免在特定区域外滑动

时间:2014-08-10 18:56:40

标签: android google-maps-android-api-2

我的应用显示了一张地图,我希望用户无法在特定区域内滑动。 所以我试图添加边界但它会让应用程序崩溃。 这是工作代码:

public class MapViewer extends Activity implements OnInfoWindowClickListener {
    private LatLng defaultLatLng = new LatLng(42.564241, 12.22759);
    private GoogleMap map;
    private int zoomLevel = 5;
    private Database db = new Database(this);

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.mapviewer);

        try {
            map = ((MapFragment) getFragmentManager().findFragmentById(R.id.map)).getMap();
            if (map != null) {
                map.setMyLocationEnabled(true);
                map.setMapType(GoogleMap.MAP_TYPE_NORMAL);
                map.getUiSettings().setRotateGesturesEnabled(false);

                map.moveCamera(CameraUpdateFactory.newLatLngZoom(defaultLatLng, zoomLevel));

                this.addMerchantMarkers(new MarkerOptions());

                map.setOnInfoWindowClickListener(this);
            }
        } catch (NullPointerException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onPause() {
        if (map != null) {
            map.setMyLocationEnabled(false);
            map.setTrafficEnabled(false);
        }
        super.onPause();
    }

    public void addMerchantMarkers(MarkerOptions mo) {
        SQLiteDatabase dbRead = db.getReadableDatabase();
        String[] columns = {"title", "addr", "lat", "lon"};
        Cursor result = dbRead.query("merchants", columns, null, null, null, null, null);

        while(result.moveToNext()) {
            String merchant = result.getString(0);
            String address = result.getString(1);
            float lat = result.getFloat(2);
            float lon = result.getFloat(3);

            LatLng pos = new LatLng(lat, lon);

            map.addMarker(mo.position(pos)
                    .title(merchant)
                    .snippet(address)
                    .icon(BitmapDescriptorFactory.fromResource(R.drawable.marker_50)));;
        }
    }
}

这是我在onCreate方法中添加的导致崩溃的代码:

    LatLngBounds.Builder builder = new LatLngBounds.Builder();
    builder.include(new LatLng(47.09194444, 18.52166666));
    builder.include(new LatLng(36.448311, 6.62555555));
    LatLngBounds bounds = builder.build();

    CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, 30);

    map.animateCamera(cu);

这是LogCat:

08-10 20:59:41.689: E/AndroidRuntime(6304): FATAL EXCEPTION: main
08-10 20:59:41.689: E/AndroidRuntime(6304): Process: com.example.myapp, PID: 6304
08-10 20:59:41.689: E/AndroidRuntime(6304): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.myapp/com.example.myapp.MapViewer}: java.lang.IllegalStateException: Error using newLatLngBounds(LatLngBounds, int): Map size can't be 0. Most likely, layout has not yet occured for the map view.  Either wait until layout has occurred or use newLatLngBounds(LatLngBounds, int, int, int) which allows you to specify the map's dimensions.
08-10 20:59:41.689: E/AndroidRuntime(6304):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2215)
08-10 20:59:41.689: E/AndroidRuntime(6304):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2264)
08-10 20:59:41.689: E/AndroidRuntime(6304):     at android.app.ActivityThread.access$800(ActivityThread.java:144)
08-10 20:59:41.689: E/AndroidRuntime(6304):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1205)
08-10 20:59:41.689: E/AndroidRuntime(6304):     at android.os.Handler.dispatchMessage(Handler.java:102)
08-10 20:59:41.689: E/AndroidRuntime(6304):     at android.os.Looper.loop(Looper.java:136)
08-10 20:59:41.689: E/AndroidRuntime(6304):     at android.app.ActivityThread.main(ActivityThread.java:5139)
08-10 20:59:41.689: E/AndroidRuntime(6304):     at java.lang.reflect.Method.invokeNative(Native Method)
08-10 20:59:41.689: E/AndroidRuntime(6304):     at java.lang.reflect.Method.invoke(Method.java:515)
08-10 20:59:41.689: E/AndroidRuntime(6304):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:796)
08-10 20:59:41.689: E/AndroidRuntime(6304):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:612)
08-10 20:59:41.689: E/AndroidRuntime(6304):     at dalvik.system.NativeStart.main(Native Method)
08-10 20:59:41.689: E/AndroidRuntime(6304): Caused by: java.lang.IllegalStateException: Error using newLatLngBounds(LatLngBounds, int): Map size can't be 0. Most likely, layout has not yet occured for the map view.  Either wait until layout has occurred or use newLatLngBounds(LatLngBounds, int, int, int) which allows you to specify the map's dimensions.
08-10 20:59:41.689: E/AndroidRuntime(6304):     at mut.b(Unknown Source)
08-10 20:59:41.689: E/AndroidRuntime(6304):     at oxp.a(Unknown Source)
08-10 20:59:41.689: E/AndroidRuntime(6304):     at oxi.a(Unknown Source)
08-10 20:59:41.689: E/AndroidRuntime(6304):     at oyf.b(Unknown Source)
08-10 20:59:41.689: E/AndroidRuntime(6304):     at grl.onTransact(SourceFile:92)
08-10 20:59:41.689: E/AndroidRuntime(6304):     at android.os.Binder.transact(Binder.java:361)
08-10 20:59:41.689: E/AndroidRuntime(6304):     at com.google.android.gms.maps.internal.IGoogleMapDelegate$a$a.animateCamera(Unknown Source)
08-10 20:59:41.689: E/AndroidRuntime(6304):     at com.google.android.gms.maps.GoogleMap.animateCamera(Unknown Source)
08-10 20:59:41.689: E/AndroidRuntime(6304):     at com.example.myapp.MapViewer.onCreate(MapViewer.java:59)
08-10 20:59:41.689: E/AndroidRuntime(6304):     at android.app.Activity.performCreate(Activity.java:5231)
08-10 20:59:41.689: E/AndroidRuntime(6304):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
08-10 20:59:41.689: E/AndroidRuntime(6304):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2169)
08-10 20:59:41.689: E/AndroidRuntime(6304):     ... 11 more

10 个答案:

答案 0 :(得分:78)

您可以使用MapLoadedCallBack;

map.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
  @Override
  public void onMapLoaded() {
      map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 30));
  }
});

并且您也可以使用此事件发生在上面。

  map.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() {
        @Override
        public void onCameraChange(CameraPosition arg0) {
        map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 30));
  }
});

答案 1 :(得分:52)

您可以按照日志中的建议来防止错误发生:

" ...使用newLatLngBounds(LatLngBounds,int,int,int),它允许您指定地图的尺寸​​。"

额外的参数是屏幕的宽度和高度。以下是您的更新代码的外观:

LatLngBounds.Builder builder = new LatLngBounds.Builder();
builder.include(new LatLng(47.09194444, 18.52166666));
builder.include(new LatLng(36.448311, 6.62555555));
LatLngBounds bounds = builder.build();

// begin new code:
int width = getResources().getDisplayMetrics().widthPixels;
int height = getResources().getDisplayMetrics().heightPixels;
int padding = (int) (width * 0.12); // offset from edges of the map 12% of screen

CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, width, height, padding);
// end of new code

map.animateCamera(cu);

答案 2 :(得分:7)

onMapReady()的文档明确指出你必须等待onMapReadyCallback和ViewTreeObserver.OnGlobalLayoutListener:

  

请注意,这并不能保证地图已经过布局。因此,在调用回调方法时可能尚未确定映射的大小。如果您需要知道尺寸或在API中调用需要知道尺寸的方法,请获取地图的视图并注册ViewTreeObserver.OnGlobalLayoutListener。

这很不方便,但一种方法是使用RxJava。您可以发出两个observable,第一个在onMapReady中,第二个在onGlobalLayout中,然后将它们压缩在一起并执行您需要做的任何事情。通过这种方式,您可以确定两个回调都被触发了。

答案 3 :(得分:4)

  

引起:java.lang.IllegalStateException:使用时出错   newLatLngBounds(LatLngBounds,int):地图大小不能为0.很可能,   地图视图尚未显示布局。要么等到布局   已发生或使用newLatLngBounds(LatLngBounds,int,int,int)   允许您指定地图的尺寸​​。

来自文档https://developer.android.com/reference/com/google/android/gms/maps/CameraUpdateFactory.html#newLatLngBounds(com.google.android.gms.maps.model.LatLngBounds,int)

  

在地图出现之前,请勿使用此相机更新更换相机   经历布局(为了使这种方法正确地确定   适当的边界框和缩放级别,地图必须有一个大小)。   否则将抛出IllegalStateException。它不是   足以使地图可用(即getMap()返回一个   非空对象);包含地图的视图也必须经历过   布局使其尺寸已确定。如果你不能   确定已发生这种情况,请使用newLatLngBounds(LatLngBounds,int,   改为int,int)并手动提供地图的尺寸​​。

注意:getMap()可能会返回null。最好在初始化GoogleMap对象之前检查Google Play服务的可用性。

答案 4 :(得分:4)

Caused by: java.lang.IllegalStateException: Map size should not be 0. Most likely, layout has not yet occured for the map view.

此错误背后的原因是因为Map布局尚未完成,您应该在调用中实现OnMapReadyCallback,这将确保您的代码仅在布局完成后运行或实现以下回调

map.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() {
    @Override
    public void onCameraChange(CameraPosition arg0) {
    map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 30));
}
});

重复链接

moveCamera with CameraUpdateFactory.newLatLngBounds crashes

IllegalStateException map size should not be 0

如果有办法合并此链接,我不会这样做。我希望我帮助

答案 5 :(得分:3)

这对我有用:

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    mMapFragment.getMapAsync(this);
}

@Override
public void onMapReady(GoogleMap googleMap) {
    mMapFragment.getView().getViewTreeObserver().addOnGlobalLayoutListener(this);
}

@Override
public void onGlobalLayout() {
    mMapFragment.getView().getViewTreeObserver().removeOnGlobalLayoutListener(this);
    //Only after onMapReady & onGlobalLayout newLatLngBounds will be available
    initMapPosition(); //newLatLngBounds here
}

答案 6 :(得分:3)

我已经创建了一种方法将两个回调:onMapReady和onGlobalLayout组合成一个单独的observable,只有当两个事件都被触发时才会发出。

https://gist.github.com/abhaysood/e275b3d0937f297980d14b439a8e0d4a

public final class OnMapAndLayoutReady {

private OnMapAndLayoutReady() {
}

/**
 * Converts {@link OnMapReadyCallback} to an observable.
 * Note that this method calls {@link MapView#getMapAsync(OnMapReadyCallback)} so you there is no
 * need to initialize google map view manually.
 */
private static Observable<GoogleMap> loadMapObservable(final MapView mapView) {
    return Observable.create(new Observable.OnSubscribe<GoogleMap>() {
        @Override
        public void call(final Subscriber<? super GoogleMap> subscriber) {
            OnMapReadyCallback mapReadyCallback = new OnMapReadyCallback() {
                @Override
                public void onMapReady(GoogleMap googleMap) {
                    subscriber.onNext(googleMap);
                }
            };
            mapView.getMapAsync(mapReadyCallback);
        }
    });
}

/**
 * Converts {@link ViewTreeObserver.OnGlobalLayoutListener} to an observable.
 * This methods also takes care of removing the global layout listener from the view.
 */
private static Observable<MapView> globalLayoutObservable(final MapView view) {
    return Observable.create(new Observable.OnSubscribe<MapView>() {
        @Override
        public void call(final Subscriber<? super MapView> subscriber) {
            final ViewTreeObserver.OnGlobalLayoutListener globalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {
                    view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                    subscriber.onNext(view);
                }
            };
            view.getViewTreeObserver().addOnGlobalLayoutListener(globalLayoutListener);
        }
    });
}

/**
 * Takes {@link #globalLayoutObservable(MapView)} and {@link #loadMapObservable(MapView)} and zips their result.
 * This means that the subscriber will only be notified when both the observables have emitted.
 */
public static Observable<GoogleMap> onMapAndLayoutReadyObservable(final MapView mapView) {
    return Observable.zip(globalLayoutObservable(mapView), loadMapObservable(mapView), new Func2<MapView, GoogleMap, GoogleMap>() {
        @Override
        public GoogleMap call(MapView mapView, GoogleMap googleMap) {
            return googleMap;
        }
    });
}
}

答案 7 :(得分:1)

将调用放在runnable中并将其发布到views处理程序,如下所示:

    mapFragment.getView().post(new Runnable() {
        @Override
        public void run() {
            map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds.build(), CAMERA_PADDING));
        }
    });

当布局视图时,runnable将执行并正确定位地图。 ahmadalibalochs回答的问题是,在正确定位之前,你会看到地球的闪光。将其发布到视图处理程序,可以解决此问题。

答案 8 :(得分:0)

标记为正确的答案不是100%正确。我认为,如果由于连接而无法加载地图,或者地图更新得太快以致于无法完全完成加载,那仍然会失败。 我已经使用了addOnGlobalLayoutListener。

ConstraintLayout mapLayout = (ConstraintLayout)findViewById(R.id.activityLayout);
mapLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener()
{
    @Override
    public void onGlobalLayout()
    {
         //Your map code
    }
});

答案 9 :(得分:0)

在 Kotlin 中,这对我有用。

以下代码,有时会抛出异常:

override fun onMapReady(googleMap: GoogleMap) {
    mMap = googleMap
    //mMap.uiSettings.isZoomControlsEnabled = true
    mMap?.uiSettings?.isMyLocationButtonEnabled = true
    mMap?.isMyLocationEnabled = true
    val cameraUpdate = CameraUpdateFactory.newLatLngBounds(bounds, padding)
    mMap?.animateCamera(cameraUpdate) 
}

使用 newLatLngBounds(LatLngBounds, int) 时出错:地图大小不能为 0。很可能,地图视图尚未进行布局。要么等到布局发生,要么使用 newLatLngBounds(LatLngBounds, int, int, int) 来指定地图的尺寸​​

此代码防止了错误:

override fun onMapReady(googleMap: GoogleMap) {
    mMap = googleMap
    //mMap.uiSettings.isZoomControlsEnabled = true
    mMap?.uiSettings?.isMyLocationButtonEnabled = true
    mMap?.isMyLocationEnabled = true
    mMap?.setOnMapLoadedCallback {
        val cameraUpdate = CameraUpdateFactory.newLatLngBounds(bounds, padding)
        mMap?.animateCamera(cameraUpdate)
    }
}

在运行我的 CameraUpdate 函数之前等待 onMapLoadedCallback