なるようになるかも

力は多くの場合、その人の思いを超えない。

マテリアルなナビゲーションドロワー。

今のGoogle製のアプリはおおよそこういう見た目になっています。

記事の端末は若干古く、記事のコメントにあるSSのようにステータスバー領域までナビゲーションドロワーが被ってくるようなのがマテリアルなやつらしいです。

で、これどう作るんでしょうね。

NavigationDrawerテンプレート

NavigationDrawerテンプレートでプロジェクトを作ってもこういう動きにはなりません。それどころか、ハンバーガーボタンが動かなくて違和感が酷いです。(自分の環境が最新になってないだけだったらすみません)

これはandroid.support.v4.app.ActionBarDrawerToggleが廃止され、android.support.v7.app.ActionBarDrawerToggleへ移行したのが原因です。

ドキュメントがまだ整備されていなくて、Creating a Navigation Drawer | Android Developersの記述も古いままなんですよね…。

ic_drawer.pngをゴミ箱に投げ捨てて、インポートするActionBarDrawerTogglev4からv7に変えましょう。

//android.support.v7.ActionBarDrawerToggle
mDrawerToggle = new ActionBarDrawerToggle(getActivity(), 
    mDrawerLayout, 
    R.string.navigation_drawer_open, 
    R.string.navigation_drawer_close)

ただこれも過渡期の、ハンバーガーボタンが矢印にアニメーションするやつで、今風ではないのです。

ActionBarの上にドロワーを載せるには?

どう頑張っても無理だと思うんです。ビュー階層がそもそも違うので。

アプリケーションの最上層には、Window#getDecorView()で取得できる、android:id/dector_content_parentがあって、android:id/action_bar_containerandroid:id/contentxmlで指定したレイアウト)はその子要素となっているはずなのです。

というわけで、適当にGmailアプリのビュー階層をダンプしてみました。

f:id:quesera2:20150110133817p:plain

com.google.android.gm:id/action_bar_rootの下にandroid:id/contentがあります。ドロワーはその子要素となっています。

ActionBarっぽく見えてるのはさらにその下にあるcom.google.android.gm:id/mail_toolbarというただのViewです。

これはサポートパッケージのActionBarActivityを使った上で、

<item name="windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>

ActionBarを消し去った上で、さらに代替としてToolbarを使っているっ挙動っぽい。

ioschedの実装を読もう

マテリアルデザインのお手本とされているioschedを読んでみます。

google/iosched · GitHub

前読んだときは、UI部分はスルーしてました。

src/main/res/values/styles.xmlより、

<style name="Theme.IOSched.Base" parent="Theme">
  ...(省略)
  <item name="windowActionBar">false</item>
  <item name="android:windowNoTitle">true</item>
  ...(省略)
</style>

<style name="Theme.IOSched.WithNavDrawer" parent="Theme.IOSched" />

src/main/res/values-v21/styles.xmlより、

<style name="Theme.IOSched.WithNavDrawer" parent="Theme.IOSched">
  <item name="android:statusBarColor">@android:color/transparent</item>
</style>

アクションバー消してるのは予想通り。stylesで空のテーマを作っておいて、v21だけステータスバーを透明にしてます。v21なんですね。4.4のimmersive mode(あまり知らない)だと思ってたので意外。

src/main/com/google.samples/apps/iosched/ui/BaseActivity.javaより、

// Primary toolbar and drawer toggle
private Toolbar mActionBarToolbar;

protected Toolbar getActionBarToolbar() {
  if (mActionBarToolbar == null) {
    mActionBarToolbar = (Toolbar) findViewById(R.id.toolbar_actionbar);
      if (mActionBarToolbar != null) {
        setSupportActionBar(mActionBarToolbar);
      }
    }
  return mActionBarToolbar;
}

ですよねー。

setSupportActionBar()ってもうこのために存在するメソッドとしか思えませんでしたし…。

setSupportActionBar()というのは、引数に渡したandroid.support.v7.widget.ToolbarをActionBarとして利用するメソッドです。

本物のActionBarをThemeで非表示にした上で、レイアウト上にあるToolbarをActionBarとして利用することによって、画面全体にナビゲーションドロワーが表示されるようにしているのです。

private void goToNavDrawerItem(int item) {
  Intent intent;
  switch (item) {
    case NAVDRAWER_ITEM_MY_SCHEDULE:
      intent = new Intent(this, MyScheduleActivity.class);
      startActivity(intent);
      finish();
      break;
...(以下略)

ドロワーからアイテム選択されたらstartActivity()した後、自身をfinish()してます。

つまり、ドロワーのルートになる全てのレイアウトがToolbarNavigationDrawerのリスト部分をincludeしてるんですね。ドロワー内のリストの選択状態は各Activityで管理なんでしょうか。

ドロワーが閉じてる最中にstartActivity()したら見た目の整合性取れないよね?と思ったら、postDelayed()startActivity()にディレイを掛けて、ドロワーが閉じた後に移動するよう見せかけてます。

これ微妙すぎるのでは…。

ActionBarActivityを使わない場合

Activity | Android Developers

API Lv21以降はsetActionBar()setSupportActionBar()と同じことができます。

ただし、この引数のToolbarandroid.support.v7.widget.Toolbarではなく、 android.widget.Toolbarです。両者は全く別物です…。