Drawerの使い方

Drawerの使い方

依存追加

implementation 'androidx.drawerlayout:drawerlayout:1.0.0'
implementation 'com.google.android.material:material:1.1.0-alpha05'   // for NavigationView

以下のようなdrawerを作る方法。

f:id:ishikota:20190402113812g:plain

Drawerのlayout定義

menu定義

Drawerのメニューにあたる各行はmenu.xml形式で定義していく。
それぞれ、Icon, titleをセットしてく。
<item android:title="SubSection">のように入れ子を作ると、
上の画像にあるようなdividerを引いたりしてくれる。

res/menu/activity_main_drawer.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
            android:id="@+id/nav_item1"
            android:icon="@drawable/ic_menu_camera"
            android:title="Item1"/>
    <item
            android:id="@+id/nav_item2"
            android:icon="@drawable/ic_menu_gallery"
            android:title="Item2"/>
    <item
            android:id="@+id/nav_item3"
            android:icon="@drawable/ic_menu_manage"
            android:title="Item3"/>

    <item android:title="SubSection">
        <menu>
            <item
                    android:id="@+id/nav_item4"
                    android:icon="@drawable/ic_menu_send"
                    android:title="Item4"/>
            <item
                    android:id="@+id/nav_item5"
                    android:icon="@drawable/ic_menu_slideshow"
                    android:title="Item5"/>
        </menu>
    </item>
</menu>

header定義

DrawerのMenu上に表示されるHeaderセクション。layout.xmlで好きに定義できる。

res/layout/nav_header_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:background="#1e88e5"
        android:theme="@style/ThemeOverlay.AppCompat.Dark"
        android:layout_width="match_parent"
        android:layout_height="176dp">

    <ImageView
            android:id="@+id/image_icon"
            android:layout_width="72dp"
            android:layout_height="72dp"
            android:paddingTop="8dp"
            android:scaleType="centerCrop"
            android:layout_margin="16dp"
            app:srcCompat="@mipmap/ic_launcher"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
    />

    <TextView
            android:id="@+id/text_name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingTop="@dimen/nav_header_vertical_spacing"
            android:text="Android Studio"
            android:textAppearance="@style/TextAppearance.AppCompat.Body1"
            android:layout_marginLeft="16dp"
            android:layout_marginRight="16dp"
            app:layout_constraintTop_toBottomOf="@id/image_icon"
            app:layout_constraintLeft_toLeftOf="parent"
    />

    <TextView
            android:id="@+id/text_address"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="android.studio@android.com"
            android:layout_marginLeft="16dp"
            android:layout_marginRight="16dp"
            app:layout_constraintTop_toBottomOf="@id/text_name"
            app:layout_constraintLeft_toLeftOf="parent"
    />

    <Button
            android:id="@+id/button_toggle_menu"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:background="?android:attr/selectableItemBackground"
            app:layout_constraintTop_toTopOf="@id/text_name"
            app:layout_constraintBottom_toBottomOf="@id/text_address"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
    />

    <ImageView
            android:id="@+id/ic_toggle_menu"
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:layout_margin="16dp"
            android:src="@drawable/ic_arrow_drop_down_white_24dp"
            app:layout_constraintTop_toTopOf="@id/button_toggle_menu"
            app:layout_constraintBottom_toBottomOf="@id/button_toggle_menu"
            app:layout_constraintRight_toRightOf="@id/button_toggle_menu"
    />

</androidx.constraintlayout.widget.ConstraintLayout>

Drawerをlayoutに埋め込む

rootにDrawerLayout, 一番下にNavigationViewを追加.
Drawer連携させるのでToolbarも加えておく。

res/layout/activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        tools:openDrawer="start">

    <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

        <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                app:popupTheme="@style/AppTheme.PopupOverlay"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"/>

         ...

    </androidx.constraintlayout.widget.ConstraintLayout>

    <com.google.android.material.navigation.NavigationView
            android:id="@+id/nav_view"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            android:fitsSystemWindows="true"
            app:headerLayout="@layout/nav_header_my_main"
            app:menu="@menu/activity_main_drawer"/>

</androidx.drawerlayout.widget.DrawerLayout>

Activity側

class MyMainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_my_main)
        setSupportActionBar(toolbar)

        val toggle = ActionBarDrawerToggle(
            this,
            drawer_layout,
            toolbar,
            R.string.navigation_drawer_open,  // <string name="navigation_drawer_open">Open navigation drawer</string>
            R.string.navigation_drawer_close  // <string name="navigation_drawer_close">Close navigation drawer</string>
        )
        drawer_layout.addDrawerListener(toggle)
        toggle.syncState()
        nav_view.setNavigationItemSelectedListener(this)
        setupHeader()
    }

    override fun onBackPressed() {
        if (drawer_layout.isDrawerOpen(GravityCompat.START)) {
            drawer_layout.closeDrawer(GravityCompat.START)
        } else {
            super.onBackPressed()
        }
    }

    override fun onNavigationItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {
            // for R.menu.activity_main_drawer
            R.id.nav_camera -> {
                /** do something **/
            }
            R.id.nav_gallery -> {
                /** do something **/
            }
            R.id.nav_slideshow -> {
                /** do something **/
            }
            R.id.nav_manage -> {
                /** do something **/
            }
            R.id.nav_share -> {
                /** do something **/
            }
            R.id.nav_send -> {
                /** do something **/
            }

            // for R.menu.activity_sub_main_drawer
            R.id.nav_item1 -> {
                /** do something **/
            }
            R.id.nav_item2 -> {
                /** do something **/
            }
            R.id.nav_item3 -> {
                /** do something **/
            }
            R.id.nav_item4 -> {
                /** do something **/
            }
            R.id.nav_item5 -> {
                /** do something **/
            }
        }
        drawer_layout.closeDrawer(GravityCompat.START)
        return true
    }

    private fun setupHeader() {
        val header = nav_view.getHeaderView(0)

        // update header content dynamically
        header.image_icon.setImageResource(R.drawable.ic_launcher_background)
        header.text_name.text = "Kota Ishimoto"
        header.text_address.text = "kota-ishimoto@google.com"
        header.text_name

        // switch menu content dynamically
        header.button_toggle_menu.setOnClickListener {
            val isMainMenu = nav_view.menu.findItem(R.id.nav_camera) != null
            nav_view.menu.clear()
            nav_view.inflateMenu(
                if (isMainMenu) R.menu.activity_sub_main_drawer else R.menu.activity_main_drawer
            )
            header.ic_toggle_menu.setImageResource(
                if (isMainMenu) R.drawable.ic_arrow_drop_up_white_24dp else R.drawable.ic_arrow_drop_down_white_24dp
            )
        }
    }
}

headerのviewにアクセスして動的にpropertyをいじりたければ、
nav_view.getHeaderView(0)でheader layoutのrootViewにアクセスできるので、
そこから好きなviewを取り出していじれば良い。

上のコードでは加えて、 動的にmenuの内容を切り替える(playstoreみたいな挙動)挙動を,
header.button_toggle_menu.setOnClickListenerのあたりで実装してる。

注意するのは、nav_view.inflateMenuする前にnav_view.menu.clear()をすること。
これをしないと、前のmenuの下に新しいmenuが追加されてしまう。