NavigationDrawerをつかって、スライド式のメニューを実装する
Androidアプリケーションのアクションバーの左側に表示できるアプリケーションアイコンには、アップナビゲーションを設定することができました。新しくサポートパッケージに追加された、NavigationLayoutを使うことで、UPナビゲーションの代わりに画面左からニュッと出てくるメニューを表示させることができます。
このメニューをナビゲーションドロワーと呼びます。本エントリでは、ナビゲーションドロワーの使い方を紹介していきます。
ナビゲーションドロワーの使いどころ
ナビゲーションドロワーは今の流行のUIだと言えるでしょう。さて、このUIはどのような時に効果的に利用出来るのでしょうか。たとえば、Androidにはトップレベルのアプリ画面を切り替える方法として、ナビゲーションドロワーの他にViewPagerとTabを利用したものがあります。このViewPagerとTabを利用したケースと、ナビゲーションドロワーを利用したケースについて見てみましょう。
※ViewPagerとタブを利用したUIについては、C83で頒布した「ドロイドの奇妙なSDK」で紹介しています。
ViewPager+Tabを利用したケースでは、トップレベルのアプリ画面が3つ以下である場合に効果的に利用できます。それ以上の画面を持つ場合や、複雑な画面階層を持つ場合にはナビゲーションドロワ-が効果的でしょう。例えば、次のようなケースでナビゲーションドロワーが効果的です。
ナビゲーションドロワーを配置する
それでは、早速ナビゲーションドロワーを使ってみましょう。
ナビゲーションドロワーはサポートライブラリに含まれています。まずはアプリケーションプロジェクトにサポートライブラリを追加します。プロジェクトを右クリックで表示される、「Android Tools」の「Add Support Library」から追加しましょう。
サポートライブラリが追加できたら、次にLayoutファイルにナビゲーションドロワーを使う為のDrawerLayoutを追加します。DrawerLayoutはレイアウトのルートに配置しなければなりません。
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent" > <!-- The main content view --> <LinearLayout android:id="@+id/left_draw" android:layout_width="match_parent" android:layout_height="match_parent" /> <!-- The navigation drawer --> <LinearLayout android:id="@+id/left_drawer" android:layout_width="240dp" android:layout_height="match_parent" android:layout_gravity="left" android:background="#333" > <Button android:id="@+id/drawer_button" android:layout_width="match_parent" android:layout_height="48dp" android:text="drawer button" /> </LinearLayout> </android.support.v4.widget.DrawerLayout>
DrawerLayoutの中にはメインで表示するViewとナビゲーションドロワーとして表示するViewを用意します。メインで表示するViewのwidthとheightはmatch_parentとしておきましょう。ナビゲーションドロワーとして表示するViewは次の決まりに従う必要があります。
- layout_gravityに”start“または”left“または”right“をもつこと。
- start : LTR/RTLによって、右から左からのドロワ-を切り替える。
- left : 画面左側からドロワーを表示する。
- right : 画面右側からドロワーを表示する。
- layout_widthを240dp以上320dp以下で作成すること。
- ドロワー内コンテンツの1行の高さは48dpを下回らないこと。
ナビゲーションドロワーとして表示するコンテンツはListViewである必要はありません。必要であれば、LinearLayoutなどの入れ子にしたListViewを使い、要素のカスタマイズを行うとよいでしょう。ここでは、ListViewを利用すると煩雑な処理が増えることからLinearLayoutを使用しています。
上記レイアウトを設定するのみで、次のような実行結果がえられます。
ナビゲーションドロワーをコントロールする
始めにナビゲーションドロワーにまつわる操作を確認しましょう。アップナビゲーションでのナビゲーションドロワーの開閉は、アップナビゲーションへの登録を行った時のみになります。
- ナビゲーションドロワーを出すとき
- アップナビゲーションボタンをタップ(設定時)
- 画面枠の左外側からのスワイプ操作
- 引っ込めるとき
- アップナビゲーションボタンをタップ(設定時)
- ドロワー外をタップする
- ドロワーを左側へスワイプ操作(ドロワー外でのスワイプは無効)
- BACKキーを押下
ナビゲーションドロワーは、画面を一目見てもユーザーからは実装されているかどうかわかりません。そのため、アクションバーに表示するアプリケーションアイコンへメニューアイコンのようなアイコンを追加することを推奨しています。
このアイコンは特別に作成する必要ありません。ここの右上でダウンロードできる画像をresディレクトリ以下に置く事でアプリケーション内で合成して表示してくれます。ダウンロードできる画像はhdpi/mdpi/xdpiディレクトリを含んでいますので、res以下に上書きコピーするとよいでしょう。
ナビゲーションドロワーを使う時には、ActionBarDrawerToggleクラスを一緒に使うとよいでしょう。ActionBarDrawerToggleクラスを使うと、ナビゲーションドロワーの「開ききった」「閉じきった」「スライド中」「ナビゲーションドロワーの状態が変わった」という4つのイベントを受ける手段や、ナビゲーションドロワーアイコンの設定やアニメーションなどを設定できます。ActivityのonCreateで実行する初期化処理をみてみましょう。
private ActionBarDrawerToggle mDrawerToggle; private DrawerLayout mDrawer; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ((Button)findViewById(R.id.drawer_button)).setOnClickListener(this); mDrawer = (DrawerLayout) findViewById(R.id.drawer_layout); mDrawerToggle = new ActionBarDrawerToggle(this, mDrawer, R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close) { @Override public void onDrawerClosed(View drawerView) { Log.i(LOGTAG, "onDrawerClosed"); } @Override public void onDrawerOpened(View drawerView) { Log.i(LOGTAG, "onDrawerOpened"); } @Override public void onDrawerSlide(View drawerView, float slideOffset) { // ActionBarDrawerToggleクラス内の同メソッドにてアイコンのアニメーションの処理をしている。 // overrideするときは気を付けること。 super.onDrawerSlide(drawerView, slideOffset); Log.i(LOGTAG, "onDrawerSlide : " + slideOffset); } @Override public void onDrawerStateChanged(int newState) { // 表示済み、閉じ済みの状態:0 // ドラッグ中状態:1 // ドラッグを放した後のアニメーション中:2 Log.i(LOGTAG, "onDrawerStateChanged new state : " + newState); } }; mDrawer.setDrawerListener(mDrawerToggle); // UpNavigationアイコン(アイコン横の<の部分)を有効に // NavigationDrawerではR.drawable.drawerで上書き getActionBar().setDisplayHomeAsUpEnabled(true); // UpNavigationを有効に getActionBar().setHomeButtonEnabled(true); }
8行目にて、ActionBarDrawerToggleクラスを作成しています。ActionBarDrawerToggleクラスのコンストラクタには、第1引数から順に、Activityのインスタンス、DrawerLayoutのインスタンス、ナビゲーションドロワー用のアイコン、open時のアクセシビリティ、close時のアクセシビリティを指定します。アクセシビリティについては、この記事を参考にどうぞ。
12,17,22,29行目のメソッドはそれぞれ、「開ききった」「閉じきった」「スライド中」「ナビゲーションドロワーの状態が変わった」といったイベントを受け取るメソッドです。スライド中をハンドリングできるonDrawerSlideメソッドをオーバーライドするときだけ注意が必要です。ActionBarDrawerToggleクラスの同メソッドにおいて、ナビゲーションドロワー用のアイコンのアニメーション処理を実装しています。そのため、25行目のようにsuper.onDrawerSlide()を呼び出すようにしておきましょう。
つづいて、38行目のsetDrawerListenerメソッドにてDrawerLayoutへ作成したActionBarDrawerToggleを設定しています。
ActionBarDrawerToggleクラスを使う場合には、onConfigurationChangedメソッドやonOptionsItemSelectedメソッドのイベントを渡してあげる必要があります。続くソースコードがそれぞれのイベントを流す処理になります。
@Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); mDrawerToggle.syncState(); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); mDrawerToggle.onConfigurationChanged(newConfig); } @Override public boolean onOptionsItemSelected(MenuItem item) { // ActionBarDrawerToggleにandroid.id.home(up ナビゲーション)を渡す。 if (mDrawerToggle.onOptionsItemSelected(item)) { return true; } return super.onOptionsItemSelected(item); } @Override public void onClick(View v) { mDrawer.closeDrawers(); }
4行目にて、アクティビティの状態とActionBarDrawerToggleの状態をsyncさせるための、syncStateメソッドを呼び出しています。
10,17行目にてonConfigurationCangedとonOptionsItemSelectedのイベントをActionBarDrawerToggleへ流しています。特に、UPナビゲーションによってナビゲーションドロワーを開閉する場合にはActoinBarDrawerToggleクラスのonOptionsItemSelectedメソッドが重要になりますので忘れないようにしましょう。
最後に26行目にて、ナビゲーションドロワーに作成したボタン押下時にドロワーを閉じるcloseDrawersメソッドを実装しています。
Effective Androidでは、本エントリで紹介出来なかったナビゲーションドロワー内のコンテンツのレイアウト構成などについても詳しく紹介していきます。おたのしみに!