【问题标题】:Add bounds to map to avoid swiping outside a certain region为地图添加边界以避免在某个区域之外滑动
【发布时间】:2014-08-10 18:56:39
【问题描述】:

我的应用显示了一张地图,我希望用户不能在某个区域上滑动。 所以我正在尝试添加边界,但它会使应用程序崩溃。 这是工作代码:

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

【问题讨论】:

  • 完成,我编辑了我的帖子,谢谢。
  • 使用 newLatLngBounds(LatLngBounds, int) 时出错:地图大小不能为 0。很可能,地图视图的布局尚未发生。要么等到布局发生,要么使用 newLatLngBounds(LatLngBounds, int, int, int) 来指定地图的尺寸​​。看起来你的地图还没有准备好。还要检查 google play 服务的可用性。
  • 我使用了 newLatLngBounds(bounds, 30, 10, 10) 并且它没有崩溃,但生成的地图不反映我设置的边界。另外我不知道如何使用这 3 个 int 值:(
  • MapViewer.java 第 59 行是什么。
  • 第 59 行有 map.animateCamera(cu);

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


【解决方案1】:

你可以使用 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));
  }
});

【讨论】:

  • 不幸的是,即使我们将moveCamera 放入onMapLoaded 中,我们仍然会遇到此崩溃。我认为地图已加载,但在某些设备上,它可能没有完全布局。所以将handler.post 放入onMapLoaded 对它有很大帮助。但我们仍然看到崩溃,即使它更低。
  • 这对我有用,但我可以看到在默认位置加载的地图,然后稍微延迟后移动到所需位置。当我改用 addOnGlobalLayoutListener 时,它在视觉上看起来好多了。
【解决方案2】:

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

"...使用 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);

【讨论】:

  • 一直在寻找可以在 Xamarin.Android 上运行的解决方案,并且效果很好。另外,使用屏幕宽度百分比的想法,无论我们的目标屏幕尺寸是什么,填充都是相同的,这是对我之前代码的一个很好的改进。非常感谢,帕特里克!
  • 这对我来说是唯一可行的方法。所有其他解决方案都不适用于某些设备。
  • 谢谢。在显示布局之前,animateCameramoveCamera 会毫无例外地工作吗?
  • 问题是为什么你认为地图会覆盖整个屏幕!?可能不会,对吧? int width = getResources().getDisplayMetrics().widthPixels; 这意味着您将屏幕尺寸作为 Map 的布局,但地图可能会覆盖屏幕的 10%,并且它可能与屏幕的右上角对齐,而不是与 int padding = (int) (width * 0.12); 12 的中心对齐% 填充
【解决方案3】:

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

请注意,这并不能保证地图已经过布局。因此,调用回调方法的时间可能尚未确定地图的大小。如果需要知道维度或者调用API中需要知道维度的方法,获取地图的View,同时注册一个ViewTreeObserver.OnGlobalLayoutListener。

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

【解决方案4】:
Caused by: java.lang.IllegalStateException: Map size should not be 0. Most likely, layout has not yet occured for the map view.

这个错误背后的原因是因为地图布局没有完成,你应该在你的调用中实现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

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

【讨论】:

  • “您应该在调用中实现 OnMapReadyCallback,这将确保您的代码仅在您的布局完成后运行或实现以下回调”,请不要在您的答案中添加额外的误导行。这与布局准备就绪无关。它只监听相机移动,这也可能是用户交互。
【解决方案5】:

原因: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 服务的可用性。

【讨论】:

  • 我使用以下代码解决了崩溃问题:pastebin.com/E5g2CAjS 但填充值似乎没有任何影响。我仍然可以在其他国家/地区的地图上移动...它不会阻止某个区域的地图。
  • @smartmouse 阅读文档,上面突出显示的部分是您原来的问题
  • 我不使用 setOnMapLoadedCallback 处理吗?
  • @smartmouse 你处理什么?
  • 使用 setOnMapLoadedCallback 我检查地图是否经过布局。我错了吗?
【解决方案6】:

这对我有用:

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

【讨论】:

  • 有时我发现了一个异常com.google.maps.api.android.lib6.common.apiexception.c: 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.
【解决方案7】:

我创建了一种方法来将两个回调: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;
        }
    });
}
}

【讨论】:

    【解决方案8】:

    将调用放在一个可运行文件中并将其发布到视图处理程序,如下所示:

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

    当视图布置好后,runnable 将执行并正确定位地图。 ahmadalibalochs 回答的问题在于,在地球正确定位之前,您会看到地球的闪光。将其发布到视图处理程序,即可解决此问题。

    【讨论】:

    • "mapFragment.getView().post" 不,这并不意味着“当视图布置好后,runnable 将执行”。 “mapFragment.getView().post” 表示Runnable 中的任何内容都将在 UI 线程中执行,并且不保证视图被布局
    【解决方案9】:

    标记为正确的答案并非 100% 正确。我认为如果地图由于连接性而永远不会加载,或者地图更新得如此之快以至于它永远不会完全完成加载,这仍然会失败。 我已经使用了 addOnGlobalLayoutListener。

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

    【讨论】:

      【解决方案10】:

      在 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

      【讨论】:

        猜你喜欢
        • 2016-01-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-10-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多