96

新しいAndroidGoogleMapsAPIを利用しています。

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オブジェクトに描画するのに最適な場所ですか、それともマーカーを描画してカメラの位置を別の場所に設定する必要がありますか?

4

20 に答える 20

133

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);
    }
});
于 2012-12-10T11:30:27.627 に答える
57

これらの答えは問題ありませんが、私は別のアプローチ、より単純なアプローチを選びます。マップがレイアウトされた後にのみメソッドが機能する場合は、それを待つだけです。

map.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
    @Override
    public void onMapLoaded() {
        map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 30));
    }
});
于 2014-03-20T00:27:58.220 に答える
52

OK私はこれを解決しました。ここに記載されているように、APIはレイアウト前に使用することはできません。

使用する正しいAPIは次のように説明されています。

注:マップのレイアウト後にカメラを移動するために使用する場合は、より単純なメソッドnewLatLngBounds(boundary、padding)のみを使用してCameraUpdateを生成します。レイアウト中に、APIはバウンディングボックスを正しく投影するために必要なマップの表示境界を計算します。比較すると、APIは渡した引数から表示境界を計算するため、マップがレイアウトされる前であっても、より複雑なメソッドnewLatLngBounds(境界、幅、高さ、パディング)によって返されるCameraUpdateをいつでも使用できます。

この問題を解決するために、画面サイズを計算し、幅と高さを

public static CameraUpdate newLatLngBounds(
    LatLngBounds bounds, int width, int height, int padding)

これにより、バウンディングボックスの事前レイアウトを指定できるようになりました。

于 2012-12-03T22:28:55.430 に答える
34

解決策はそれよりも簡単です...。

// 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代わりに、ビューでグローバルリスナーをキャッチして使用してください。他の方法を使用して事前にバインドされたサイズ(事前レイアウト)を設定することは、スクリーンデバイスではなく、ビューのサイズを計算する必要があることを意味します。これらは、マップを全画面表示し、フラグメントを使用しない場合にのみ一致します。

于 2013-01-28T09:51:59.733 に答える
15

これは私の修正です。その場合、マップがロードされるまで待ってください。

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));
        }
    });
}
于 2014-12-30T09:50:10.547 に答える
15

アプリのさまざまな場所で同じコードを使用する必要があったため、受け入れられた回答は機能しませんでした。

カメラが変わるのを待つ代わりに、マップサイズを指定するという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));

それが他の誰かに役立つことを願っています!

于 2015-06-30T16:17:48.183 に答える
14

マーカーの追加と削除はレイアウトの完了前に行うことができますが、カメラを移動することはできません(newLatLngBounds(boundary, padding)OPの回答に記載されているように使用する場合を除く)。

おそらく、最初のカメラ更新を実行するのに最適な場所は、 GoogleのサンプルコードOnGlobalLayoutListenerに示されているワンショットを使用することです。たとえば、MarkerDemoActivity.javaからの次の抜粋を参照してください。setUpMap()

// 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));
        }
    });
}
于 2013-01-26T00:40:42.333 に答える
8

コメントで指摘されているように、受け入れられた答えは少しハッキーです。それを実装した後、分析で許容できる量を超えるIllegalStateExceptionログに気づきました。OnMapLoadedCallback私が使用した解決策は、が実行されるを追加することCameraUpdateです。コールバックがに追加されますGoogleMap。マップが完全に読み込まれると、カメラの更新が実行されます。

これにより、カメラの更新を実行する前に、マップにズームアウトされた(0,0)ビューが短時間表示されます。ただし、これはクラッシュを引き起こしたり、文書化されていない動作に依存したりするよりも許容できると思います。

于 2014-02-19T17:38:38.813 に答える
6
 map.moveCamera(CameraUpdateFactory.newLatLngZoom(bounds.getCenter(),10));

これを使って私のために働いた

于 2015-02-07T07:30:05.963 に答える
4

ごくまれに、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));
                    }
                });
            }
        }
    });
}
于 2014-07-15T15:29:17.830 に答える
4

最近のバージョンのGoogleMapsSDK(9.6以降)で機能し、 onCameraIdleListenerに基づくさまざまなアプローチを使用しました。これまで見てきたように、これonCameraIdleは常に。の後に呼び出されるコールバックメソッド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
    */
}
于 2016-11-30T08:39:23.077 に答える
4

2つのコールバック(onMapReadyとonGlobalLayout)を1つのオブザーバブルに結合する方法を作成しました。これらは、両方のイベントがトリガーされたときにのみ発行されます。

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

于 2017-04-17T18:59:57.503 に答える
2

わかりました、私は同じ問題に直面しています。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)代わりに使用して、マップの寸法を手動で指定してください。

それはかなりまともな解決策だと思います。それが誰かを助けることを願っています。

于 2013-07-24T04:21:38.730 に答える
1

ユーザーとの対話が開始されるのを待つ必要がある場合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();

開始が速いことに加えて、最初に表示される世界地図はなく、次にカメラが移動します。マップは、指定された場所から直接開始されます。

于 2014-12-17T09:50:41.477 に答える
0

別のアプローチは次のようになります(最上位のビューが、という名前のFrameLayoutであると仮定します。rootContainerただし、タイプや名前に関係なく、常に最上位のコンテナーを選択する限り機能します)。

((FrameLayout)findViewById(R.id.rootContainer)).getViewTreeObserver()
    .addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
        public void onGlobalLayout() {
            layoutDone = true;
        }
    });

カメラの機能を変更して、それが機能する場合にのみ機能するようlayoutDonetrueすると、追加の機能を追加したり、layoutListenerハンドラーにロジックを接続したりすることなく、すべての問題を解決できます。

于 2013-01-26T00:44:47.833 に答える
0

私はこれが機能し、他のソリューションよりも単純であることがわかりました。

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

これにより、レイアウトの実行後に呼び出しが再試行されます。おそらく、無限ループを回避するための安全性を含めることができます。

于 2015-02-17T12:52:21.117 に答える
0

次のようなものを使用しないのはなぜですか。

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);
}
于 2018-08-11T11:15:37.210 に答える
0

OnCameraChangeListener()で説明されているように、非推奨になりましたが、現在は非推奨にsetOnCameraChangeListenerなっています。したがって、次の3つのmtehodのいずれかに置き換える必要があります。

  • GoogleMap.OnCameraMoveStartedListener
  • GoogleMap.OnCameraMoveListener
  • GoogleMap.OnCameraIdleListener

私の場合、OnCameraIdleListenerそれはどんな動きでも何度も呼び出されたので、私はそれを使用し、内部でそれを削除しました。

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

アップデート

googleMap.setOnCameraIdleListenerマップが表示されたときに呼び出されることはなかったが、保持されていたため、プロジェクトから削除しましgoogleMap.setOnCameraIdleListener(clusterManager)た。

于 2018-12-19T12:12:27.077 に答える
0

Googleマップリポジトリには、活用できるヘルパークラスがあります。これは、レイアウトとマップの両方の準備が整うのを待ってから、GoogleMapでコールバックを通知します。

元のソースはここにあります:

https://github.com/googlemaps/android-samples/blob/7ee737b8fd6d39c77f8b3716ba948e1ce3730e61/ApiDemos/java/app/src/main/java/com/example/mapdemo/OnMapAndViewReadyListener.java

Kotlinの実装もあります。

https://github.com/googlemaps/android-samples/blob/master/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/OnMapAndViewReadyListener.kt

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);
    }
}
}
于 2018-12-20T22:08:15.603 に答える
0

mMap.animateCameraを呼び出そうとしても同じエラーが発生しました。以下に示すように、 onMapReady関数でsetOnMapLoadedCallbackコールバックを呼び出すことで解決しました

public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;

   // other codes go there

    mMap.setOnMapLoadedCallback(() -> {
            //Your code where exception occurs goes here...
            
        });
}

これは正常に機能するはずです。

于 2020-08-09T03:53:27.747 に答える