Shoeisha Technology Media

CodeZine(コードジン)

特集ページ一覧

イベントリスナーの追加とMapFragmentのカスタマイズ

Web APIで楽々Androidアプリ(7)

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2014/01/16 14:00

 Android端末の通信機能と、国内外のさまざまなサイトで提供されているWeb APIを活用すれば、楽しいアプリをかんたんに作ることができます。この連載では、多種多様なWeb APIの紹介と、そのAPIを利用したサンプルアプリの制作を通じて、Androidアプリの実践的な開発を解説します。前回は、地図の中心位置の最寄り駅を表示するところまでを解説しました。第7回の本稿では、地図を移動した場合やマーカーの吹き出し(情報ウィンドウ)をタップした場合のイベント処理と、MapFragmentのカスタマイズを解説します。

目次

対象読者

 Androidアプリケーションの開発を始めたい方で、JavaとEclipseの基本的な知識がある方を対象とします。

地図のイベント処理

 地図の移動やマーカーの吹き出しのタップを判断するには、そのイベントを監視する、イベントリスナーと呼ばれる処理を追加します。

地図の位置を変更したときのイベントリスナー

 処理手順の1から4まではすでに解説しましたので、今回は、5と6の部分を解説します。

  1. 地図の中心位置を取得して、Web APIにアクセスする
  2. 結果を解析する
  3. マーカーを追加する
  4. マーカーと駅情報を保存する
  5. 地図の位置を変更したときのイベントリスナーを追加する
  6. マーカー(吹き出し)をタップしたときのイベントリスナーを追加する

 次のようにActivityには、前回までのコードの修正と追加部分があります。

MainActivity.javaの一部
public class MainActivity extends Activity implements LoaderCallbacks<String> {
    ~中略~

    // 地図の中心位置(1)
    private CameraPosition centerPosition = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ~中略~
        if (savedInstanceState == null) {
            ~中略~
        }

        // 地図の中心位置を取得する(2)
        centerPosition = googleMap.getCameraPosition();

        // APIのURLを準備してローダーを初期化する(3)
        execMoyori(centerPosition);

        // 地図を移動したときのリスナーを追加する(4)
        googleMap.setOnCameraChangeListener(
            // 無名クラス(5)
            new OnCameraChangeListener() {
                @Override
                public void onCameraChange(CameraPosition cameraPosition) {
                }
            }
        );

        // 吹き出しのクリックリスナーを追加する(6)
        googleMap.setOnInfoWindowClickListener(new OnInfoWindowClickListener() {
            @Override
            public void onInfoWindowClick(Marker marker) {
                // 駅情報の取り出し(7)
                EkiInfo e = ekiMarkerMap.get(marker);
                Toast ts = Toast.makeText(getBaseContext(), e.name + "("
                        + e.distance + "m)\n" + "前の駅:" + e.prev + "\n次の駅:"
                        + e.next + "\n" + e.line, Toast.LENGTH_LONG);
                ts.setGravity(Gravity.TOP, 0, 200);
                ts.show();
            }
        });
    }

    // APIのURLを準備してローダーを初期化する(前回からパラメータを変更)
    public void execMoyori(CameraPosition centerPosition) {
        Bundle bundle = new Bundle();
        // 緯度
        bundle.putString("y", Double.toString(centerPosition.target.latitude));
        // 経度
        bundle.putString("x", Double.toString(centerPosition.target.longitude));
        bundle.putString("moyori",
                "http://express.heartrails.com/api/json?method=getStations&");
        // LoaderManagerの初期化
        getLoaderManager().restartLoader(0, bundle, this);
    }

    ~中略~
}

 まず、前回のコードからの変更点を説明しておきましょう。

 MainActivityクラスに、地図の中心位置を保存するフィールドを追加しています(1)。また、地図の中心位置を取得する処理(2)を、execMoyoriメソッドから分離させました。これは、execMoyoriメソッドの、Web APIのURLを準備してローダーを初期化する処理を、後述するイベントのコールバック処理のなかでも利用したいためです。

 次に、イベントリスナーの処理を説明します。

 地図の移動の追随は、地図の位置を変更した際に発生するイベントを利用します。このイベントの発生時に何らかの処理を行うには、setOnCameraChangeListenerメソッドを使ってイベントリスナーを登録します(4)。これで、地図の位置が変更された際に、指定したメソッドがコールバックされるようになります。

 setOnCameraChangeListenerメソッドの引数は、OnInfoWindowClickListenerインターフェースを実装したクラスです。このインターフェースには、地図の位置を変更した際にコールバックされるonCameraChangeというメソッドのみ定義されています。

 このようなイベントリスナーの場合、無名クラスを使って、OnInfoWindowClickListenerインターフェースを実装するようにすると、簡潔に記述できます(5)

 吹き出しに対するイベントリスナーも、同様に追加します(6)。ここでは、OnInfoWindowClickListenerインターフェースを実装する無名クラスを利用しています。

 吹き出しをタップすると、onInfoWindowClickメソッドがコールバックされます。そのメソッドの引数のmarkerは、タップされたマーカーとなっていますので、HashMapのekiMarkerMapから、最寄り駅情報を取り出しています(7)

地図に追随してマーカーを表示する

 最後に、地図の位置を変更した際にコールバックされるonCameraChangeメソッド内の処理を追加します。

MainActivity.javaの一部
public class MainActivity extends Activity implements LoaderCallbacks<String> {
    ~中略~
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ~中略~
        googleMap.setOnInfoWindowClickListener(new OnInfoWindowClickListener() {
            @Override
            public void onCameraChange(CameraPosition cameraPosition) {
                // 前回から500メートルより大きく移動したら(1)
                if (0.5 < calcDistance(centerPosition, cameraPosition)) {
                    execMoyori(cameraPosition);
                    centerPosition = cameraPosition;
                }
            }
        });
        ~中略~
    }

    // 2点間の距離を求める(km)(2)
    private double calcDistance(CameraPosition a, CameraPosition b) {

        double lata = Math.toRadians(a.target.latitude);
        double lnga = Math.toRadians(a.target.longitude);

        double latb = Math.toRadians(b.target.latitude);
        double lngb = Math.toRadians(b.target.longitude);

        double r = 6378.137; // 赤道半径

        return r * Math.acos(Math.sin(lata) * Math.sin(latb)
                    + Math.cos(lata) * Math.cos(latb) * Math.cos(lngb - lnga));
    }
    ~後略~

 onCameraChangeメソッドでは、地図の中心位置が、前回保存した位置から500メールより大きくずれた場合に、Web APIを呼び出すようにしています。

 onCameraChangeメソッドの引数には、移動した最後の位置が格納されていますので、その位置と、保存しておいた位置(centerPosition)との2点間を比較しています。

 2点の緯度、経度から、距離を求めるには、地球の球体を考慮した計算が必要です(2)。計算式については、「2地点間の距離と方位角」というサイトを参考にしました。

 これで、地図を移動した場合に、追随してマーカーが表示されるようになります。


  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • WINGSプロジェクト 高江 賢(タカエ ケン)

    <WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂...

  • 山田 祥寛(ヤマダ ヨシヒロ)

    静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for ASP/ASP.NET。執筆コミュニティ「WINGSプロジェクト」代表。 主な著書に「入門シリーズ(サーバサイドAjax/XMLD...

バックナンバー

連載:Web APIで楽々Androidアプリ

もっと読む

All contents copyright © 2005-2018 Shoeisha Co., Ltd. All rights reserved. ver.1.5