SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Web APIで楽々Androidアプリ

Web APIで取得した位置情報をAndroidアプリの地図上にマーカーとして追加する

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

  • X ポスト
  • このエントリーをはてなブックマークに追加

Web APIへのアクセス

 まずは、Web APIへのアクセスの部分から説明しましょう。この処理は、連載第2回第3回で作成した、HttpAsyncLoader、ParseJsonクラスをそのまま利用します。処理の流れも前回と同じなので、ざっと見ていきましょう。

パッケージの変更

 今回は汎用的に使えるように、前述の両クラスのパッケージ名を、package com.rakuraku.android.utilに変更しています。また、ParseJsonクラスを、異なるパッケージからでも使えるようにpublic属性とします。

ParseJson.javaの一部
package com.rakuraku.android.util;

public class ParseJson {
}

URLのアクセス

 最初に、Activityの定義で、インターフェースLoaderCallbacks<String>を実装します。そして、Web APIにアクセスするコードを追加します。

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

    // 地図の中心位置を取得して、APIのURLを準備する
    public void execMoyori() {

        // 地図の中心位置の取得
        CameraPosition cameraPos = googleMap.getCameraPosition();

        Bundle bundle = new Bundle();
        // 緯度
        bundle.putString("y", Double.toString(cameraPos.target.latitude));
        // 経度
        bundle.putString("x", Double.toString(cameraPos.target.longitude));

        bundle.putString("moyori",
                "http://express.heartrails.com/api/json?method=getStations&");

        // LoaderManagerの初期化(1)
        getLoaderManager().restartLoader(0, bundle, this);
    }

    @Override
    public Loader<String> onCreateLoader(int id, Bundle bundle) {
        HttpAsyncLoader loader = null;
        switch (id) {
        case 0:
            // リクエストURLの組み立て
            String url = bundle.getString("moyori")
                        + "x=" + bundle.getString("x") + "&"
                        + "y=" + bundle.getString("y");

            loader = new HttpAsyncLoader(this, url);

            // Web APIにアクセスする(2)
            loader.forceLoad();
            break;
        }
        return loader;
    }
}

 LoaderManagerを初期化(1)した後、コールバックされるonCreateLoaderメソッドの中で、forceLoadメソッドを実行して(2)、APIにアクセスしています。

アクセス結果の解析

 APIにアクセスした後、onLoadFinishedメソッドがコールバックされますので、そのメソッドのなかで、レスポンスのJSON文字列を解析します。

 JSONの中には、最寄駅の情報が複数含まれています。後で利用しやすいように、別のコレクションオブジェクト(ArrayList)に保存しておきましょう。

EkiInfo.java
package com.rakuraku.android.ekimap;

// 最寄駅情報クラス
public class EkiInfo {
    public String name;     // 最寄駅名
    public String prev;     // 前の駅名 (始発駅の場合は null)
    public String next;     // 次の駅名 (終着駅の場合は null)
    public Double   x;      // 最寄駅の経度 (世界測地系)
    public Double   y;      // 最寄駅の緯度 (世界測地系)
    public int  distance;   // 指定の場所から最寄駅までの距離 (精度は 10 m)
    public String   line;   // 最寄駅の存在する路線名
}
ParseMoyori.java
public class ParseMoyori extends ParseJson {

    // 駅情報のリスト
    private List<EkiInfo> ekiinfo = new ArrayList<EkiInfo>();

    public List<EkiInfo> getEkiinfo() {
        return ekiinfo;
    }

    @Override
    public void loadJson(String str) {
        JsonNode root = getJsonNode(str);
        if (root != null){

            // 最寄駅のイテレータを取得する(1)
            Iterator<JsonNode> ite =
                root.path("response").path("station").elements();

            // 要素の取り出し(2)
            while (ite.hasNext()) {
                JsonNode j = ite.next();

                // 駅情報のセット(3)
                EkiInfo e = new EkiInfo();
                e.x = j.path("x").asDouble();
                e.y = j.path("y").asDouble();
                e.name = j.path("name").asText();
                e.next = j.path("next").asText();
                e.prev = j.path("prev").asText();
                e.line = j.path("line").asText();

                // 「xxxm」を数値に変換
                e.distance =
                    Integer.parseInt(j.path("distance").asText().split("m")[0]);

                // リストに追加(4)
                ekiinfo.add(e);
            }
        }
    }
}

 EkiInfoクラスは、最寄駅の情報をプロパティ風にまとめたクラスです。このクラスを通して、最寄駅の情報にアクセスします。

 JSON文字列のstationフィールド以下に、駅情報が配列として含まれています。このJsonNodeのオブジェクトの配列要素からオブジェクトを取り出すには、イテレータを利用します(1)

 elementsメソッドは、イテレータを返しますので、Whileループを使ってイテレータから要素を1つずつ取り出しています(2)。取り出した要素は、EkiInfoオブジェクトにセット(3)した後、フィールドとして定義したekiinfoリストに追加します(4)

マーカーの追加

 駅情報を、地図の上にマーカーとして追加します。

MainActivity.javaの一部
// マーカーと駅情報のHashMap(1)
private HashMap<Marker, EkiInfo> ekiMarkerMap;

@Override
public void onLoadFinished(Loader<String> loader, String body) {

    // APIの取得に失敗の場合
    if (body == null) return;

    switch (loader.getId()) {

        case 0:

            // APIの結果を解析する
            ParseMoyori parse = new ParseMoyori();
            parse.loadJson(body);

            // マーカーをいったん削除しておく
            googleMap.clear();
            ekiMarkerMap.clear();

            // APIの結果をマーカーに反映する(2)
            for (EkiInfo e : parse.getEkiinfo()) {

                Marker marker = googleMap.addMarker(new MarkerOptions()
                        .position(new LatLng(e.y, e.x))
                        .title(e.name)
                        .snippet(e.line)
                        .icon(BitmapDescriptorFactory
                                .fromResource(R.drawable.ic_train))); // (3)

                // マーカーと駅情報を保管しておく(4)
                ekiMarkerMap.put(marker, e);
            }
            break;
    }
}

 Google Mapsへのマーカーの追加は、かんたんにできるようになっています。addMarkerメソッドで、MarkerOptionsオブジェクトを追加するだけです。

 MarkerOptionsオブジェクトには、マーカーに表示するタイトルなどを設定します。設定可能な情報は、次のとおりです。

MarkerOptionsオブジェクトの主なメソッド
メソッド 設定する内容
Position 位置(必須)
Title マーカーをタップしたときに表示される文字列
Snippet タイトルの下に表示される文字列
Draggable マーカーのドラッグができるか否か。デフォルトはfalse
Visible マーカーの表示。デフォルトはtrue
Anchor マーカー画像のどこを座標値とするか。デフォルトは画像の下端中央
Icon マーカー画像

 マーカーのアイコン画像は、指定しないとデフォルトの表示ですが、任意の画像を表示することもできます(3)。今回のサンプルでは、電車マークの画像ファイルを使っています(素材として、http://free-icon.org/index.htmlで公開されているものを利用しています)。

 なお、画像ファイルは、プロジェクトのresフォルダ配下の、drawable-xxxフォルダのいずれかにコピーしておきます。

 マーカーに設定する情報は、Web APIの結果を解析した、ParseMoyoriオブジェクトのListオブジェクトから取得します。forループで、1つずつ取り出し、addMarkerメソッドでマーカーに設定しています(2)

マーカーと駅情報の保管

 このように、マーカーを追加するだけならシンプルにできるのですが、マーカーにIDのようなものを設定する機能はありません。この機能がないため、例えば、どのマーカーがタップされたのかを判断する場合に、とても不便です。

 そこで、今回のサンプルでは、マーカーとマーカーに追加する情報を、別のコレクションオブジェクトに保管しています(4)

 ここでは、HashMapオブジェクトを使っています。HashMapは、いわゆる連想配列のコレクションで、キーとそれに対応する値のペアが、1つの要素になっています。

 このサンプルでは、マーカーオブジェクトをキーに、駅情報オブジェクトをそれに対応する値としています(1)

最後に

 次回は、マーカーをタップした場合のイベントの処理や、よりアプリらしくするためのコードを追加していきます。

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
Web APIで楽々Androidアプリ連載記事一覧

もっと読む

この記事の著者

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

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

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

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

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/7517 2013/12/06 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング