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)。
最後に
次回は、マーカーをタップした場合のイベントの処理や、よりアプリらしくするためのコードを追加していきます。
