CameraUpdateFactory.newLatLngBounds가있는 moveCamera가 다운 됨
새로운 Android Google Maps API를 사용하고 있습니다.
MapFragment를 포함하는 활동을 만듭니다. 활동 onResume
에서 마커를 GoogleMap 개체에 설정 한 다음 모든 마커를 포함하는지도의 경계 상자를 정의합니다.
이것은 다음 의사 코드를 사용하고 있습니다.
LatLngBounds.Builder builder = new LatLngBounds.Builder();
while(data) {
LatLng latlng = getPosition();
builder.include(latlng);
}
CameraUpdate cameraUpdate = CameraUpdateFactory
.newLatLngBounds(builder.build(), 10);
map.moveCamera(cameraUpdate);
에 대한 호출로 map.moveCamera()
인해 응용 프로그램이 다음 스택과 충돌합니다.
Caused by: java.lang.IllegalStateException:
Map size should not be 0. Most likely, layout has not yet
at maps.am.r.b(Unknown Source)
at maps.y.q.a(Unknown Source)
at maps.y.au.a(Unknown Source)
at maps.y.ae.moveCamera(Unknown Source)
at com.google.android.gms.maps.internal.IGoogleMapDelegate$Stub
.onTransact(IGoogleMapDelegate.java:83)
at android.os.Binder.transact(Binder.java:310)
at com.google.android.gms.maps.internal.IGoogleMapDelegate$a$a
.moveCamera(Unknown Source)
at com.google.android.gms.maps.GoogleMap.moveCamera(Unknown Source)
at ShowMapActivity.drawMapMarkers(ShowMapActivity.java:91)
at ShowMapActivity.onResume(ShowMapActivity.java:58)
at android.app.Instrumentation
.callActivityOnResume(Instrumentation.java:1185)
at android.app.Activity.performResume(Activity.java:5182)
at android.app.ActivityThread
.performResumeActivity(ActivityThread.java:2732)
newLatLngBounds()
공장 방법 대신 newLatLngZoom()
방법을 사용 하면 동일한 트랩이 발생하지 않습니다.
onResume
마커를 GoogleMap 개체에 그리는 가장 좋은 장소 입니까, 아니면 마커를 그리고 다른 곳에 카메라 위치를 설정해야합니까?
OnCameraChangeListener 에서 간단한 newLatLngBounds 메소드를 사용할 수 있습니다 . 모두 완벽하게 작동하며 화면 크기를 계산할 필요가 없습니다. 이 이벤트는지도 크기 계산 후에 발생합니다 (내가 이해 한대로).
예:
map.setOnCameraChangeListener(new OnCameraChangeListener() {
@Override
public void onCameraChange(CameraPosition arg0) {
// Move camera.
map.moveCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), 10));
// Remove listener to prevent position reset on camera move.
map.setOnCameraChangeListener(null);
}
});
그 대답은 괜찮지 만 다른 접근 방식, 더 간단한 접근 방식을 선택하겠습니다. 지도가 배치 된 후에 만 메서드가 작동하는 경우 기다리십시오.
map.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
@Override
public void onMapLoaded() {
map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 30));
}
});
알았어. 로 여기에 문서화 하는 API는 사용되는 사전 배치 할 수 없습니다.
사용할 올바른 API는 다음과 같이 설명됩니다.
참고 :지도가 레이아웃을 완료 한 후 카메라를 이동하는 데 사용되는 경우 더 간단한 메소드 newLatLngBounds (boundary, padding) 만 사용하여 CameraUpdate를 생성하세요. 레이아웃 중에 API는 경계 상자를 올바르게 투영하는 데 필요한지도의 표시 경계를 계산합니다. 이에 비해 API는 전달하는 인수에서 표시 경계를 계산하기 때문에지도가 레이아웃을 완료하기 전에도 언제든지 더 복잡한 메소드 newLatLngBounds (boundary, width, height, padding)에서 반환하는 CameraUpdate를 사용할 수 있습니다.
문제를 해결하기 위해 화면 크기를 계산하고 너비와 높이를
public static CameraUpdate newLatLngBounds(
LatLngBounds bounds, int width, int height, int padding)
그러면 경계 상자 사전 레이아웃을 지정할 수 있습니다.
해결책은 그것보다 간단합니다 ....
// Pan to see all markers in view.
try {
this.gmap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 50));
} catch (IllegalStateException e) {
// layout not yet initialized
final View mapView = getFragmentManager()
.findFragmentById(R.id.map).getView();
if (mapView.getViewTreeObserver().isAlive()) {
mapView.getViewTreeObserver().addOnGlobalLayoutListener(
new OnGlobalLayoutListener() {
@SuppressWarnings("deprecation")
@SuppressLint("NewApi")
// We check which build version we are using.
@Override
public void onGlobalLayout() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
mapView.getViewTreeObserver()
.removeGlobalOnLayoutListener(this);
} else {
mapView.getViewTreeObserver()
.removeOnGlobalLayoutListener(this);
}
gmap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 50));
}
});
}
}
IllegalStateException
대신 뷰에서 글로벌 리스너를 잡아서 사용하십시오. 다른 방법을 사용하여 미리 경계 크기를 설정 (프리 레이아웃)하면 화면 장치가 아닌 THE VIEW의 크기를 계산해야합니다. 지도로 전체 화면으로 이동하고 조각을 사용하지 않는 경우에만 일치합니다.
이것은 내 수정 사항입니다.이 경우 맵이로드 될 때까지 기다리십시오.
final int padding = getResources().getDimensionPixelSize(R.dimen.spacing);
try {
mMap.animateCamera(CameraUpdateFactory.newLatLngBounds(pCameraBounds, padding));
} catch (IllegalStateException ise) {
mMap.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
@Override
public void onMapLoaded() {
mMap.animateCamera(CameraUpdateFactory.newLatLngBounds(pCameraBounds, padding));
}
});
}
마커 추가 및 제거는 사전 레이아웃 완료로 수행 할 수 있지만 카메라를 이동할 수는 없습니다 ( newLatLngBounds(boundary, padding)
OP의 답변에 명시된대로 사용 하는 경우 제외 ).
아마 초기 카메라 업데이트를 수행하는 가장 좋은 장소는 원샷을 사용하고 OnGlobalLayoutListener
같이 구글의 샘플 코드 예로부터 발췌 한 다음 코드를 참조 setUpMap()
에 MarkerDemoActivity.java :
// Pan to see all markers in view.
// Cannot zoom to bounds until the map has a size.
final View mapView = getSupportFragmentManager()
.findFragmentById(R.id.map).getView();
if (mapView.getViewTreeObserver().isAlive()) {
mapView.getViewTreeObserver().addOnGlobalLayoutListener(
new OnGlobalLayoutListener() {
@SuppressLint("NewApi") // We check which build version we are using.
@Override
public void onGlobalLayout() {
LatLngBounds bounds = new LatLngBounds.Builder()
.include(PERTH)
.include(SYDNEY)
.include(ADELAIDE)
.include(BRISBANE)
.include(MELBOURNE)
.build();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
mapView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
} else {
mapView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
mMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 50));
}
});
}
내 앱의 여러 위치에서 동일한 코드를 사용해야했기 때문에 받아 들여진 대답이 작동하지 않았습니다.
카메라가 바뀔 때까지 기다리는 대신지도 크기를 지정하라는 Lee의 제안에 따라 간단한 솔루션을 만들었습니다. 지도가 화면 크기 인 경우입니다.
// Gets screen size
int width = getResources().getDisplayMetrics().widthPixels;
int height = getResources().getDisplayMetrics().heightPixels;
// Calls moveCamera passing screen size as parameters
map.moveCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), width, height, 10));
다른 사람에게 도움이되기를 바랍니다!
받아 들여지는 대답은 의견에서 지적했듯이 약간 엉망입니다. 이를 구현 한 후 IllegalStateException
분석에서 허용 가능한 양 이상의 로그를 발견했습니다 . 내가 사용한 해결책 OnMapLoadedCallback
은 CameraUpdate
이 수행 되는 을 추가하는 것 입니다 . 콜백이 GoogleMap
. 지도가 완전히로드되면 카메라 업데이트가 수행됩니다.
이렇게하면 카메라 업데이트를 수행하기 전에지도에 축소 된 (0,0) 뷰가 잠깐 표시됩니다. 나는 이것이 충돌을 일으키거나 문서화되지 않은 행동에 의존하는 것보다 더 수용 가능하다고 생각합니다.
map.moveCamera(CameraUpdateFactory.newLatLngZoom(bounds.getCenter(),10));
이것을 사용하십시오.
매우 드물게 MapView 레이아웃이 완료되었지만 GoogleMap 레이아웃이 완료되지 않았기 때문에 ViewTreeObserver.OnGlobalLayoutListener
자체적으로 충돌을 멈출 수 없습니다. Google Play 서비스 패키지 버전 5084032에서 충돌이 발생했습니다.이 드문 경우는 내 MapView의 가시성이 동적으로 변경되어 발생할 수 있습니다.
이 문제를 해결하기 위해, 나는 임베디드 GoogleMap.OnMapLoadedCallback
에서 onGlobalLayout()
,
if (mapView.getViewTreeObserver().isAlive()) {
mapView.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
@SuppressWarnings("deprecation")
public void onGlobalLayout() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
mapView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
} else {
mapView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
try {
map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 5));
} catch (IllegalStateException e) {
map.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
@Override
public void onMapLoaded() {
Log.d(LOG_TAG, "move map camera OnMapLoadedCallback");
map.moveCamera(CameraUpdateFactory
.newLatLngBounds(bounds, 5));
}
});
}
}
});
}
최신 버전의 Google Maps SDK (9.6 이상)에서 작동하고 onCameraIdleListener를 기반으로하는 다른 접근 방식을 사용 했습니다 . 지금까지 보았 듯이 onCameraIdle
always after라는 콜백 메서드 onMapReady
입니다. 그래서 내 접근 방식은 다음 코드와 같습니다 Activity
.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// set content view and call getMapAsync() on MapFragment
}
@Override
public void onMapReady(GoogleMap googleMap) {
map = googleMap;
map.setOnCameraIdleListener(this);
// other initialization stuff
}
@Override
public void onCameraIdle() {
/*
Here camera is ready and you can operate with it.
you can use 2 approaches here:
1. Update the map with data you need to display and then set
map.setOnCameraIdleListener(null) to ensure that further events
will not call unnecessary callback again.
2. Use local boolean variable which indicates that content on map
should be updated
*/
}
두 개의 콜백 인 onMapReady와 onGlobalLayout을 결합하는 방법을 만들었습니다.
https://gist.github.com/abhaysood/e275b3d0937f297980d14b439a8e0d4a
좋아 나는 같은 문제에 직면 해있다. 내 SupportmapFragment, ABS 및 탐색 서랍이있는 조각이 있습니다. 내가 한 일은 :
public void resetCamera() {
LatLngBounds.Builder builderOfBounds = new LatLngBounds.Builder();
// Set boundaries ...
LatLngBounds bounds = builderOfBounds.build();
CameraUpdate cu;
try{
cu = CameraUpdateFactory.newLatLngBounds(bounds,10);
// This line will cause the exception first times
// when map is still not "inflated"
map.animateCamera(cu);
System.out.println("Set with padding");
} catch(IllegalStateException e) {
e.printStackTrace();
cu = CameraUpdateFactory.newLatLngBounds(bounds,400,400,0);
map.animateCamera(cu);
System.out.println("Set with wh");
}
//do the rest...
}
덧붙여서 부풀린 후 돌아 오기 전에 전화 resetCamera()
하고 onCreateView
있습니다.
이것이하는 일은 처음으로 예외를 포착하는 것입니다 (지도가 그것을 말하는 방법으로 "크기를 얻는 동안 ...). 그리고 다른 경우에는 카메라를 재설정해야합니다.지도는 이미 크기가 있고 패딩을 통해 수행합니다.
지도 레이아웃이 완료 될 때까지이 카메라 업데이트로 카메라를 변경하지 마십시오 (이 방법이 적절한 경계 상자와 확대 / 축소 수준을 올바르게 결정하려면지도에 크기가 있어야 함). 그렇지 않으면
IllegalStateException
이 던져집니다. 지도를 사용할 수있는 것만으로는 충분하지 않습니다 (즉getMap()
, null이 아닌 객체를 반환합니다). 지도가 포함 된 뷰도 크기가 결정되는 레이아웃을 거쳐야합니다. 이것이 발생했는지 확신 할 수없는 경우newLatLngBounds(LatLngBounds, int, int, int)
대신 사용 하고지도의 치수를 수동으로 제공하십시오.
꽤 괜찮은 해결책이라고 생각합니다. 누군가에게 도움이되기를 바랍니다.
가능한 사용자 상호 작용이 시작될 때까지 기다려야하는 OnMapLoadedCallback
경우 이전 답변에 설명 된대로 사용 하십시오. 그러나 필요한 것이지도의 기본 위치를 제공하는 것뿐이라면 해당 답변에 설명 된 솔루션이 필요하지 않습니다. MapFragment
및 둘 다 기본 위치를 모두 제공 할 수있는 시작시 MapView
허용 GoogleMapOptions
할 수 있습니다. 유일한 트릭은 레이아웃에 직접 포함하지 않는 것입니다. 왜냐하면 시스템은 옵션없이 그들을 호출 할 것이기 때문에 동적으로 초기화하기 때문입니다.
레이아웃에서 이것을 사용하십시오.
<FrameLayout
android:id="@+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
다음에서 조각을 교체하십시오 onCreateView()
.
GoogleMapOptions options = new GoogleMapOptions();
options.camera(new CameraPosition.Builder().target(location).zoom(15).build());
// other options calls if required
SupportMapFragment fragment = (SupportMapFragment) getFragmentManager().findFragmentById(R.id.map);
if (fragment == null) {
FragmentTransaction transaction = getFragmentManager().beginTransaction();
fragment = SupportMapFragment.newInstance(options);
transaction.replace(R.id.map, fragment).commit();
getFragmentManager().executePendingTransactions();
}
if (fragment != null)
GoogleMap map = fragment.getMap();
시작하는 것이 더 빠르다는 것 외에도 먼저 세계지도가 표시되지 않고 카메라가 두 번째로 움직입니다. 지도는 지정된 위치에서 직접 시작됩니다.
또 다른 접근 방식은 다음과 같습니다 ( rootContainer
어떤 유형이나 이름에 관계없이 항상 최상위 컨테이너를 선택하는 한 작동하지만 최상위 뷰가라는 FrameLayout이라고 가정 ).
((FrameLayout)findViewById(R.id.rootContainer)).getViewTreeObserver()
.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
public void onGlobalLayout() {
layoutDone = true;
}
});
경우에만 작업에 카메라 기능을 수정 layoutDone
IS가 true
추가 기능을 추가하거나에 로직을 연결할 필요없이 모든 문제를 해결할 layoutListener
핸들러입니다.
나는 이것이 작동하고 다른 솔루션보다 더 간단하다는 것을 알았습니다.
private void moveMapToBounds(final CameraUpdate update) {
try {
if (movedMap) {
// Move map smoothly from the current position.
map.animateCamera(update);
} else {
// Move the map immediately to the starting position.
map.moveCamera(update);
movedMap = true;
}
} catch (IllegalStateException e) {
// Map may not be laid out yet.
getWindow().getDecorView().post(new Runnable() {
@Override
public void run() {
moveMapToBounds(update);
}
});
}
}
레이아웃이 실행 된 후 다시 호출을 시도합니다. 무한 루프를 피하기 위해 안전을 포함 할 수 있습니다.
다음과 같이 사용하면 안됩니다.
CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngBounds(builder.build(), padding);
try {
map.moveCamera(cameraUpdate);
} catch (Exception e) {
int width = getResources().getDisplayMetrics().widthPixels;
int height = getResources().getDisplayMetrics().heightPixels;
cameraUpdate = CameraUpdateFactory.newLatLngBounds(builder.build(), width, height, padding);
map.moveCamera(cameraUpdate);
}
에 명시된 바와 같이 ()는 지원되지 않습니다 OnCameraChangeListener , setOnCameraChangeListener
지금은 사용되지 않습니다. 따라서 다음 세 가지 방법 중 하나로 대체해야합니다.
- GoogleMap.OnCameraMoveStartedListener
- GoogleMap.OnCameraMoveListener
- GoogleMap.OnCameraIdleListener
In my case I used OnCameraIdleListener
and inside I removed it, because it was invoked again and again on any movement.
googleMap.setOnCameraIdleListener {
googleMap.setOnCameraIdleListener(null) // It removes the listener.
googleMap.moveCamera(track)
googleMap.cameraPosition
clusterManager!!.cluster()
// Add another listener to make ClusterManager correctly zoom clusters and markers.
googleMap.setOnCameraIdleListener(clusterManager)
}
UPDATE
I removed googleMap.setOnCameraIdleListener
in my project, because it wasn't called sometimes when a map was shown, but retained googleMap.setOnCameraIdleListener(clusterManager)
.
There is a helper class in the Google Maps repo that you can leverage - it waits for both the layout and map to be ready before notifying a callback with the GoogleMap:
The original source is here:
There is a Kotlin implementation too:
public class OnMapAndViewReadyListener implements OnGlobalLayoutListener, OnMapReadyCallback {
/** A listener that needs to wait for both the GoogleMap and the View to be initialized. */
public interface OnGlobalLayoutAndMapReadyListener {
void onMapReady(GoogleMap googleMap);
}
private final SupportMapFragment mapFragment;
private final View mapView;
private final OnGlobalLayoutAndMapReadyListener devCallback;
private boolean isViewReady;
private boolean isMapReady;
private GoogleMap googleMap;
public OnMapAndViewReadyListener(
SupportMapFragment mapFragment, OnGlobalLayoutAndMapReadyListener devCallback) {
this.mapFragment = mapFragment;
mapView = mapFragment.getView();
this.devCallback = devCallback;
isViewReady = false;
isMapReady = false;
googleMap = null;
registerListeners();
}
private void registerListeners() {
// View layout.
if ((mapView.getWidth() != 0) && (mapView.getHeight() != 0)) {
// View has already completed layout.
isViewReady = true;
} else {
// Map has not undergone layout, register a View observer.
mapView.getViewTreeObserver().addOnGlobalLayoutListener(this);
}
// GoogleMap. Note if the GoogleMap is already ready it will still fire the callback later.
mapFragment.getMapAsync(this);
}
@Override
public void onMapReady(GoogleMap googleMap) {
// NOTE: The GoogleMap API specifies the listener is removed just prior to invocation.
this.googleMap = googleMap;
isMapReady = true;
fireCallbackIfReady();
}
@SuppressWarnings("deprecation") // We use the new method when supported
@SuppressLint("NewApi") // We check which build version we are using.
@Override
public void onGlobalLayout() {
// Remove our listener.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
mapView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
} else {
mapView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
isViewReady = true;
fireCallbackIfReady();
}
private void fireCallbackIfReady() {
if (isViewReady && isMapReady) {
devCallback.onMapReady(googleMap);
}
}
}
'program story' 카테고리의 다른 글
.NET은 바이트를 KB, MB, GB 등으로 쉽게 변환하는 방법을 제공합니까? (0) | 2020.08.29 |
---|---|
MySQL을 사용하여 임의의 고유 한 8 자 문자열 생성 (0) | 2020.08.29 |
부트 스트랩 팝 오버 내에 양식이 포함되어 있습니까? (0) | 2020.08.29 |
Fragment ActionBarCompat 내부의 getSupportActionBar (0) | 2020.08.29 |
$ (this)를 제외한 모든 항목 숨기기 : not in jQuery selector (0) | 2020.08.29 |