AndroidアプリでMapのスクロールに合わせてPinを移動させる


以外と情報が見つからなかったので備忘録的に書いておく。

やりたいこと

  • GoogleMapsAPIで地図を表示させてユーザーに場所を選択させるUIを作りたい
  • マップ中心にPinと円を表示して、マップをスクロールすることで場所を選ばせたい

準備

下の記事などを参考にして、GoogleMapsAPIKeyを取得〜プロジェクト作成までしておく
Androidアプリ開発でGoogleMapを利用する

ポイント

GoogleMapオブジェクトのsetOnCameraChangeListenerでマップの表示位置変更イベントを取れるので、そこにMarkerとCircleの描画処理を書いてやれば良い。
その際、描画済みのMarkerとCircleをremove()しないとどんどん増えていってしまうので注意。

キャプチャ

ソースコード


public class MapsActivity extends FragmentActivity implements OnMapReadyCallback {
    /** 地図の初期位置. */
    private static final LatLng DEFAULT_PIN_POSITION = new LatLng(35.691636, 139.701732);
    /** ズームレベル初期値. */
    private static final float DEFAULT_MAP_ZOOM = 11.0f;
    /** 円の半径初期値. */
    private static final Double DEFAULT_CIRCLE_RANGE = 5d*1000;
    private GoogleMap mMap;
    private Marker mMarker = null;
    private Circle mCircle = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_maps);
        // Obtain the SupportMapFragment and get notified when the map is ready to be used.
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);
    }

    /**
     * Manipulates the map once available.
     * This callback is triggered when the map is ready to be used.
     * This is where we can add markers or lines, add listeners or move the camera. In this case,
     * we just add a marker near Sydney, Australia.
     * If Google Play services is not installed on the device, the user will be prompted to install
     * it inside the SupportMapFragment. This method will only be triggered once the user has
     * installed Google Play services and returned to the app.
     */
    @Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;

        mMap.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() {
            @Override
            public void onCameraChange(CameraPosition cameraPosition) {
                mMarker.remove();
                mCircle.remove();
                mMarker = mMap.addMarker(new MarkerOptions().position(cameraPosition.target));
                mCircle = mMap.addCircle(createCircleOptions(cameraPosition.target, DEFAULT_CIRCLE_RANGE));
            }
        });

        mMarker = mMap.addMarker(new MarkerOptions().position(DEFAULT_PIN_POSITION));
        mCircle = mMap.addCircle(createCircleOptions(DEFAULT_PIN_POSITION, DEFAULT_CIRCLE_RANGE));
        mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(DEFAULT_PIN_POSITION, DEFAULT_MAP_ZOOM));
    }

    private static CircleOptions createCircleOptions(LatLng position, Double range){
        CircleOptions co = new CircleOptions();
        co.center(position);
        co.radius(range);
        co.fillColor(Color.parseColor("#3300FFCC")); // Circle内部の色
        co.strokeColor(Color.parseColor("#FF0000FF")); // Circle枠の色
        co.strokeWidth(2); // Circle枠の太さ
        return co;
    }
}

追記

ただこの方法だとスクロール中は表示が更新されない。
ちゃんとマップ中心に表示し続けるにはIconOverlayを被せたりする必要があるのかな・・・