2014/12/20

Google Maps Android API v2 の Marker は Drag 後に Position がアップデートされない

Google Maps Android API v2 では以下のようにドラッグ可能な Marker を作ることができます。

Marker marker = mGoogleMap.addMarker(
        new MarkerOptions()
                .position(location)
                .draggable(true));


GoogleMap クラスは Marker の情報を保存しておいてくれないので、上記のように作成した Marker のインスタンスは何らかの形で自分で保持しておかないといけないという制約があります。
ところが、Marker をドラッグ後に Marker#getPosition() を呼んでも、ドラッグ前の Position しか取得できません。 「ドラッグした」というイベントを捕まえて、自分で更新しないといけないようですちょっとイマイチな仕様だと思うのは私だけでしょうか…?


具体的には以下のような実装で、ドラッグ後のインスタンスに差し替えればよいかと思います。
// Marker を保持するための Map
private Map<String, Marker> mMarkers = new HashMap<>();

@Override
public void onActivityCreated(@Nullable final Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    mMapView.getMapAsync(new OnMapReadyCallback() {
        @Override
        public void onMapReady(GoogleMap googleMap) {
            mMap = googleMap;

            // ドラッグ検知用のリスナ登録
            googleMap.setOnMarkerDragListener(mOnMarkerDragListener);

            // 地図上に Marker を表示し、mMarkers に保持する
            for (LatLng location : mLocations) {
                Marker marker = googleMap.addMarker(
                        new MarkerOptions()
                                .position(location)
                                .draggable(true));
                mMarkers.put(marker.getId(), marker);
            }
        }
    });
}

private final GoogleMap.OnMarkerDragListener mOnMarkerDragListener = 
        new GoogleMap.OnMarkerDragListener() {
    @Override
    public void onMarkerDragStart(Marker marker) {
    }

    @Override
    public void onMarkerDrag(Marker marker) {
    }

    @Override
    public void onMarkerDragEnd(Marker marker) {
        // ドラッグが終わったら、インスタンスを差し替える
        mMarkers.put(marker.getId(), marker);
    }
};


2014/12/11

Google Play Services 6.5 でライブラリが分割されたらしいけど、今のところ正しく動作しない様子

2014/12/28 追記
2014/12/28 現在、この問題は既に解決済みです。
どうも、play-services-base への依存関係が各ライブラリに記述されていなかったようです。

表題のように Google Play Services 6.5 でライブラリが分割されたとのこと。
これまでは、Map が使いたいだけなのに Ads とか Drive とかの API も同梱しないといけなかったのが、個別に別れたわけですね。

というわけで、早速使ってみたのですが、残念ながらうまく動作しませんでした。

Setting Up Google Play Services | Android Developers によると

build.gradle
dependencies {
    compile 'com.google.android.gms:play-services:6.5.+'
}



dependencies {
    compile 'com.google.android.gms:play-services-maps:6.5.+'
    compile 'com.google.android.gms:play-services-ads:6.5.+'
}

みたいに書き換えるとオッケーと書いてあるのですが、実際にそのようにして実行してみると

Caused by: java.lang.NoSuchFieldError: com.google.android.gms.R$styleable.AdsAttrs
       at com.google.android.gms.internal.bb.(Unknown Source)
       at com.google.android.gms.internal.bh.(Unknown Source)
       at com.google.android.gms.internal.bh.(Unknown Source)
       at com.google.android.gms.internal.bh.(Unknown Source)
       at com.google.android.gms.ads.AdView.(Unknown Source)
       at java.lang.reflect.Constructor.constructNative(Native Method)
       at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
       at android.view.LayoutInflater.createView(LayoutInflater.java:586)
       at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:680)
       at android.view.LayoutInflater.rInflate(LayoutInflater.java:739)
       at android.view.LayoutInflater.rInflate(LayoutInflater.java:742)
       at android.view.LayoutInflater.rInflate(LayoutInflater.java:742)
       at android.view.LayoutInflater.inflate(LayoutInflater.java:489)
       at android.view.LayoutInflater.inflate(LayoutInflater.java:396)
       at android.view.LayoutInflater.inflate(LayoutInflater.java:352)
       at android.support.v7.app.ActionBarActivityDelegateBase.setContentView(ActionBarActivityDelegateBase.java:228)
       at android.support.v7.app.ActionBarActivity.setContentView(ActionBarActivity.java:102)
       at com.kokufu.test.MainActivity.onCreate(MainActivity.java:84)
       at android.app.Activity.performCreate(Activity.java:4465)
       at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1049)
       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1931)
       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1992)
       at android.app.ActivityThread.access$600(ActivityThread.java:127)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1158)
       at android.os.Handler.dispatchMessage(Handler.java:99)
       at android.os.Looper.loop(Looper.java:137)
       at android.app.ActivityThread.main(ActivityThread.java:4441)
       at java.lang.reflect.Method.invokeNative(Native Method)
       at java.lang.reflect.Method.invoke(Method.java:511)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:823)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:590)
       at dalvik.system.NativeStart.main(Native Method)

NoSuchFieldError が出てしまいました。
明らかに分割ミスであると思われます。うーん残念。

この問題、力技で回避する強者もいるみたいですが、
これまで通り全部コミコミにしておけば(以下)正常に動作しますので、修正されるのを待つのが得策かと思います。

build.gradle
dependencies {
    compile 'com.google.android.gms:play-services:6.5.+'
}