Android には標準でもたくさんのサポート ライブラリが用意されており、UX / UI を極める上では欠かせないものとなっていますね。
サポート ライブラリの他にも、さわるだけで楽しくなるようなメニューやボタンの動作などを、独自のライブラリとしてオープンソース(以下、OSS)で公開しているものもあります。
今回はそんなポップで楽しい OSS ライブラリを一覧でまとめているサイトを発見したので、Android で作る地図アプリに組み込んでみました。次の画像のようにメニューをカスタマイズできます。
ちなみに、私は初めて OSS のライブラリを Android で使用したので、その手順も記述しておきます。

使用したライブラリ
今回は、BoomMenu という、メニューをカスタマイズできるライブラリを使って地図の表示場所を切り替えるアプリを作ってみます。
「画像で探すView系 Android ライブラリまとめ(2016/08)」のブログ記事に掲載されている UX / UI の GitHub リポジトリを参考にさせていただきました。このリポジトリでは、レイアウトだけでなくメニューやボタンの操作、Loading 中のアニメーションまでたくさんの中から画像で探すことができます。見ているだけでも飽きない楽しいリポジトリです。
作成したアプリ
BoomMenu から選択したボタンに従って、あらかじめ設定しておいた(ブックマークした)場所を表示するというアプリです。
地図の表示と、場所をブックマークする機能は ArcGIS Runtime SDK for Android を使って作成します。ブックマーク機能ではアプリの地図操作から新規の場所をブックマークできますが、今回はあらかじめコードでブックマークを設定しておきます。ソースコードは Esriジャパンの GitHub をご覧ください。

BoomMenu を実装してみよう
BoomMenu の使い方は、このリポジトリの Wiki に英語で記されていますが、特に実装が必要な部分を記録として残しておきます。
Android Studioで空のプロジェクトを作成してから、コーディングや定義が必要なファイルは全部で 3 つです。
build.gradle ファイル
BoomMenu の参照先のリポジトリを定義します。参照先は README に記述がありました。大体どの OSS もここに書いてあるようです。
Android で OSS を使うための定義は他にもいくつかあるようですが、今回は gradle ファイルを編集しました。1行だけです!
Maven など、他の定義方法については「GitHubのOSSから見るbuild.gradleでライブラリのバージョンを管理する方法」という記事がわかりやすかったです。
dependencies {
compile 'com.nightonke:boommenu:2.1.0'
…
}
activity_main.xml ファイル (レイアウトファイル)
最低限必要なのは com.nightonke.boommenu.BoomMenuButton の id と layout の定義です。
app:・・・の部分は、ボタンにテキストを付けたり、並び替えのために必要な定義です。このライブラリは、ボタンの個数や並び方、アニメーションの動きの制御を定数で定義できるので、独自のカスタマイズもしやすくなっています。次で定義しているのは、テキスト付の丸いボタンを6つ、下の画像の配置で表示するための定義です。

<com.nightonke.boommenu.BoomMenuButton
android:id="@+id/bmb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:bmb_buttonEnum="textInsideCircle"
app:bmb_piecePlaceEnum="piecePlace_dot_6_4"
app:bmb_buttonPlaceEnum="buttonPlace_sc_6_4"
/>
MainActivityファイル
ここでレイアウト ファイルに設定したボタンの処理を実装していきます。
MainActivity でやることは、ボタンのデザインを実装することと、選択されたボタンに対し処理を実装することの 2 つです。Android で基本的なボタン処理を実装する流れと同じです。注意点は、アイコンと一緒に表示する文字の表示を内側と外側のどちらにするかでオブジェクトが違うことです。レイアウト ファイルの定義と合わせてください。
<SPAN class="comment token">/////////////////////////////////////</SPAN>
<SPAN class="comment token">// BoomMenu ボタン作成</SPAN>
<SPAN class="comment token">/////////////////////////////////////</SPAN>
<SPAN class="comment token">// レイアウトから BoomMenu のオブジェクトを作成する</SPAN>
BoomMenuButton bmb <SPAN class="operator token">=</SPAN> <SPAN class="punctuation token">(</SPAN>BoomMenuButton<SPAN class="punctuation token">)</SPAN> <SPAN class="token function">findViewById</SPAN><SPAN class="punctuation token">(</SPAN>R<SPAN class="punctuation token">.</SPAN>id<SPAN class="punctuation token">.</SPAN>bmb<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
bmb<SPAN class="punctuation token">.</SPAN><SPAN class="token function">setButtonEnum</SPAN><SPAN class="punctuation token">(</SPAN>ButtonEnum<SPAN class="punctuation token">.</SPAN>TextOutsideCircle<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">for</SPAN> <SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">int</SPAN> i <SPAN class="operator token">=</SPAN> <SPAN class="number token">0</SPAN><SPAN class="punctuation token">;</SPAN> i <SPAN class="operator token"><</SPAN> bmb<SPAN class="punctuation token">.</SPAN><SPAN class="token function">getButtonPlaceEnum</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="token function">buttonNumber</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN> i<SPAN class="operator token">++</SPAN><SPAN class="punctuation token">)</SPAN> <SPAN class="punctuation token">{</SPAN>
<SPAN class="comment token">// ボタンに設定するテキストやアイコンを定義します</SPAN>
TextOutsideCircleButton<SPAN class="punctuation token">.</SPAN>Builder builder <SPAN class="operator token">=</SPAN> null<SPAN class="punctuation token">;</SPAN>
<SPAN class="keyword token">if</SPAN><SPAN class="punctuation token">(</SPAN>i <SPAN class="operator token">%</SPAN> <SPAN class="number token">2</SPAN> <SPAN class="operator token">==</SPAN> <SPAN class="number token">0</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">{</SPAN>
<SPAN class="comment token">// 偶数/奇数によって表示アイコンを分ける</SPAN>
builder <SPAN class="operator token">=</SPAN> <SPAN class="keyword token">new</SPAN> <SPAN class="token class-name">TextOutsideCircleButton<SPAN class="punctuation token">.</SPAN>Builder</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN>
<SPAN class="punctuation token">.</SPAN><SPAN class="token function">normalImageRes</SPAN><SPAN class="punctuation token">(</SPAN>R<SPAN class="punctuation token">.</SPAN>drawable<SPAN class="punctuation token">.</SPAN>mitt<SPAN class="punctuation token">)</SPAN>
<SPAN class="punctuation token">.</SPAN><SPAN class="token function">normalText</SPAN><SPAN class="punctuation token">(</SPAN>mNavigationDrawerItemTitles<SPAN class="punctuation token">[</SPAN>i<SPAN class="punctuation token">]</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN><SPAN class="keyword token">else</SPAN><SPAN class="punctuation token">{</SPAN>
builder <SPAN class="operator token">=</SPAN> <SPAN class="keyword token">new</SPAN> <SPAN class="token class-name">TextOutsideCircleButton<SPAN class="punctuation token">.</SPAN>Builder</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN>
<SPAN class="punctuation token">.</SPAN><SPAN class="token function">normalImageRes</SPAN><SPAN class="punctuation token">(</SPAN>R<SPAN class="punctuation token">.</SPAN>drawable<SPAN class="punctuation token">.</SPAN>bat<SPAN class="punctuation token">)</SPAN>
<SPAN class="punctuation token">.</SPAN><SPAN class="token function">normalText</SPAN><SPAN class="punctuation token">(</SPAN>mNavigationDrawerItemTitles<SPAN class="punctuation token">[</SPAN>i<SPAN class="punctuation token">]</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN>
bmb<SPAN class="punctuation token">.</SPAN><SPAN class="token function">addBuilder</SPAN><SPAN class="punctuation token">(</SPAN>builder<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN>
<SPAN class="comment token">/////////////////////////////////////</SPAN>
<SPAN class="comment token">// BoomMenu ボタンイベント定義</SPAN>
<SPAN class="comment token">/////////////////////////////////////</SPAN>
bmb<SPAN class="punctuation token">.</SPAN><SPAN class="token function">setOnBoomListener</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">new</SPAN> <SPAN class="token class-name">OnBoomListener</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN> <SPAN class="punctuation token">{</SPAN>
@Override
<SPAN class="keyword token">public</SPAN> <SPAN class="keyword token">void</SPAN> <SPAN class="token function">onClicked</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="keyword token">int</SPAN> index<SPAN class="punctuation token">,</SPAN> BoomButton boomButton<SPAN class="punctuation token">)</SPAN> <SPAN class="punctuation token">{</SPAN>
<SPAN class="comment token">// ボタンを選択したときの処理をcallする</SPAN>
<SPAN class="token function">selectBookmark</SPAN><SPAN class="punctuation token">(</SPAN>index<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="punctuation token">}</SPAN>
@Override
<SPAN class="keyword token">public</SPAN> <SPAN class="keyword token">void</SPAN> <SPAN class="token function">onBackgroundClick</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN> <SPAN class="punctuation token">{</SPAN><SPAN class="punctuation token">}</SPAN>
@Override
<SPAN class="keyword token">public</SPAN> <SPAN class="keyword token">void</SPAN> <SPAN class="token function">onBoomWillHide</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN> <SPAN class="punctuation token">{</SPAN><SPAN class="punctuation token">}</SPAN>
@Override
<SPAN class="keyword token">public</SPAN> <SPAN class="keyword token">void</SPAN> <SPAN class="token function">onBoomDidHide</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN> <SPAN class="punctuation token">{</SPAN><SPAN class="punctuation token">}</SPAN>
@Override
<SPAN class="keyword token">public</SPAN> <SPAN class="keyword token">void</SPAN> <SPAN class="token function">onBoomWillShow</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN> <SPAN class="punctuation token">{</SPAN><SPAN class="punctuation token">}</SPAN>
@Override
<SPAN class="keyword token">public</SPAN> <SPAN class="keyword token">void</SPAN> <SPAN class="token function">onBoomDidShow</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN> <SPAN class="punctuation token">{</SPAN><SPAN class="punctuation token">}</SPAN>
<SPAN class="punctuation token">}</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN><SPAN class="line-numbers-rows"><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN></SPAN>おまけ:地図のブックマーク機能を実装する
今回使用した地図表示機能とブックマーク機能は、ArcGIS Runtime SDK for Android に標準で備わっているものです。
このアプリで使用したブックマーク作成方法と呼び出し方についてコードで解説します。地図の表示については ArcGIS 開発リソース集の地図表示の実装をご覧ください。
ArcGIS Runtime SDK for Android のブックマーク オブジェクトに、場所情報(緯度経度)と名称を設定します。
<SPAN class="comment token">/**
* ブックマークリストを作成する
*/</SPAN>
<SPAN class="keyword token">private</SPAN> <SPAN class="keyword token">void</SPAN> <SPAN class="token function">addDefaultBookmarks</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN> <SPAN class="punctuation token">{</SPAN>
<SPAN class="comment token">// BookmarkList のオブジェクトを ArcGISMap オブジェクトから作成する</SPAN>
mBookmarks <SPAN class="operator token">=</SPAN> mMap<SPAN class="punctuation token">.</SPAN><SPAN class="token function">getBookmarks</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
Viewpoint viewpoint<SPAN class="punctuation token">;</SPAN>
<SPAN class="comment token">// 東京ドーム</SPAN>
viewpoint <SPAN class="operator token">=</SPAN> <SPAN class="keyword token">new</SPAN> <SPAN class="token class-name">Viewpoint</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="number token">35.704651999999996</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="number token">139.75302499999998</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="number token">6e3</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
mBookmark <SPAN class="operator token">=</SPAN> <SPAN class="keyword token">new</SPAN> <SPAN class="token class-name">Bookmark</SPAN><SPAN class="punctuation token">(</SPAN>mNavigationDrawerItemTitles<SPAN class="punctuation token">[</SPAN><SPAN class="number token">0</SPAN><SPAN class="punctuation token">]</SPAN><SPAN class="punctuation token">,</SPAN> viewpoint<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
mBookmarks<SPAN class="punctuation token">.</SPAN><SPAN class="token function">add</SPAN><SPAN class="punctuation token">(</SPAN>mBookmark<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
<SPAN class="comment token">// 阪神甲子園球場</SPAN>
viewpoint <SPAN class="operator token">=</SPAN> <SPAN class="keyword token">new</SPAN> <SPAN class="token class-name">Viewpoint</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="number token">34.721206</SPAN><SPAN class="punctuation token">,</SPAN><SPAN class="number token">135.361622</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="number token">6e3</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
mBookmark <SPAN class="operator token">=</SPAN> <SPAN class="keyword token">new</SPAN> <SPAN class="token class-name">Bookmark</SPAN><SPAN class="punctuation token">(</SPAN>mNavigationDrawerItemTitles<SPAN class="punctuation token">[</SPAN><SPAN class="number token">1</SPAN><SPAN class="punctuation token">]</SPAN><SPAN class="punctuation token">,</SPAN> viewpoint<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
mBookmarks<SPAN class="punctuation token">.</SPAN><SPAN class="token function">add</SPAN><SPAN class="punctuation token">(</SPAN>mBookmark<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN>
・・・<SPAN class="line-numbers-rows"><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN></SPAN>- ブックマーク オブジェクトから表示位置情報を取得する
mMapView<SPAN class="punctuation token">.</SPAN><SPAN class="token function">setViewpointAsync</SPAN><SPAN class="punctuation token">(</SPAN>mBookmarks<SPAN class="punctuation token">.</SPAN><SPAN class="token function">get</SPAN><SPAN class="punctuation token">(</SPAN>position<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="token function">getViewpoint</SPAN><SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">;</SPAN> <SPAN class="comment token">// positionは選択されたボタン(index)のナンバー</SPAN><SPAN class="line-numbers-rows"><SPAN></SPAN></SPAN>
BoomMenu のイベントで選択されたボタンの index 番号を渡して、あらかじめ定義しておいたブックマーク オブジェクトから指定した index 番号の位置情報を取得し、このポイントに合わせて地図の表示位置を移動します。
MapView というのは、地図全体を管理する ArcGIS Runtime SDK for Android のオブジェクトです。これに表示位置情報を設定することで指定の場所を中心に地図を表示することができます。
おわりに
初めて OSS を使って Android でレイアウトを定義してみました。いつも私は初めて使用するライブラリでは使い方に苦戦するのですが、これは比較的簡単で直感的にできるかなと思いました。見せ方もポップで、実装していても面白いですし、何よりこういった UI を画像で探せるリポジトリが貴重だとも思いました。
地図を扱う上では、UX / UI というものは大変重要です。Android とこういったポップなライブラリを使って、使いやすく楽しい地図アプリを模索していきましょう!
関連リンク