Web APIへのアクセス
まずは、Web APIへのアクセスの部分から説明しましょう。この処理は、連載第2回、第3回で作成した、HttpAsyncLoader、ParseJsonクラスをそのまま利用します。処理の流れも前回と同じなので、ざっと見ていきましょう。
パッケージの変更
今回は汎用的に使えるように、前述の両クラスのパッケージ名を、package com.rakuraku.android.utilに変更しています。また、ParseJsonクラスを、異なるパッケージからでも使えるようにpublic属性とします。
package com.rakuraku.android.util; public class ParseJson { }
URLのアクセス
最初に、Activityの定義で、インターフェースLoaderCallbacks<String>を実装します。そして、Web APIにアクセスするコードを追加します。
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)に保存しておきましょう。
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; // 最寄駅の存在する路線名 }
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)。
マーカーの追加
駅情報を、地図の上にマーカーとして追加します。
// マーカーと駅情報の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オブジェクトには、マーカーに表示するタイトルなどを設定します。設定可能な情報は、次のとおりです。
メソッド | 設定する内容 |
---|---|
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)。
最後に
次回は、マーカーをタップした場合のイベントの処理や、よりアプリらしくするためのコードを追加していきます。