본문 바로가기

안드로이드/코틀린(Kotlin)

[Android : Kotlin] Navigation Drawer를 사용해서 메뉴를 만들어보자

300x250

평소에는 닫혀있다가 사용자의 메뉴 버튼 클릭이나 스와이프(Swipe)를 사용하여 슬라이드 형식으로 '서랍'처럼 레이아웃의 옆쪽에서 튀어나오는 메뉴가 Navigation Drawer를 사용해 만든 메뉴이며 대표적으로는 Gmail 어플리케이션의 좌측 상단 메뉴에 쓰인 레이아웃이다.

Navigation Drawer는 DrawerLayout을 사용하여 구성하며 Drawer에 Navigation 항목들이 결합된 형태이다.

 

Gmail 어플리케이션의 좌측 상단 아이콘 클릭시 메뉴가 나온다

 

1. DrawerLayout

DrawerLayout은 주화면과 특정 액션으로 주화면 위에 나타나는 보조화면 즉, Drawer가 있는데 두 화면의 구성은 다음과 같다.

 

DrawerLayout 화면 구성

Drawer가 나타나지 않은 주 화면에서 버튼 이벤트 등으로 Drawer를 나타나게 할 수 있으며, view에 layout_gravity = 'left' 혹은 'right'로 Drawer가 나타나는 방향과 layout_width로 Drawer의 size를 정할 수 있다.

 

 

기본적으로 Drawer를 여는 방법은 스와이프지만 DrawerLayout에서 제공되는 openDrawer()함수 및 closeDrawer()함수로 버튼 등을 사용하여 열거나 닫을 수 있다.

 

이 DrawerLayout을 기본으로 하여 Navigation Drawer를 구현하게 된다.

 

2. Dependency 추가 및 액션 바 제거

DrawerLayout은 Support Library v4에 포함된 클래스이므로 build.gradle (app)에 Support Library를 추가해준다.

implementation 'androidx.legacy:legacy-support-v4:1.0.0' // DrawerLayout
implementation 'com.google.android.material:material:1.0.0' // NavigationView

또한, 액션바를 사용하지 않고 툴바를 사용할 것이고 상태바 영역까지 Drawer를 보여지게 할 것이므로 /res/values/styles.xml의 AppTheme의 parent 부분을 다음과 같이 수정한다.

아래의 방법은 안드로이드 5.0 즉 v21이상에서만 동작한다.

<resources>
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>

        <!-- 상태바까지 view를 보여줄 수 있다-->
        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
        <!-- 상태바 색상-->
        <item name="android:statusBarColor">#33D59F9F</item>
    </style>
</resources>

 

  • android:windowDrawsSystemBarBackgrounds : true 시 상태바 까지 view를 보여줄 수 있다. 단, 네비게이션 바는 제외한다.

3. Navigation Drawer 영역

Drawer영역의 Navigation View 즉 Navigation Drawer에서 보여줄 영역을 추가한다.

메뉴에서 사용될 아이콘은 미리/res/drawable/에 추가한다.

(Clip Art에서 무료 사용 아이콘을 다운받을 수 있다.)

 

File → New → Vector Asset → ClipArt

 

Drawer의 영역은 헤더 부분과 Navigation부분으로 나누어져있으며 헤더부분을 사용하지 않을경우 헤더 레이아웃은 만들지 않아도 된다.

 

DrawerLayout 영역

먼저, Navigation 영역 레이아웃을 만든다. 

Navigation영역은 반드시 res하위에 menu폴더를 만들고 그 하위에 레이아웃을 만들어야한다.

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id = "@+id/account"
      android:icon = "@drawable/ic_account"
      android:title = "@string/menu_account"
/>
<item android:id = "@+id/item2"
      android:icon = "@drawable/ic_account"
      android:title = "item2"
/>
<item android:id = "@+id/item3"
      android:icon = "@drawable/ic_account"
      android:title = "item3"
/>
</menu>

/res/menu/main_drawer_navigation.xml

 

 

그 다음 헤더 영역 레이아웃을 만든다.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/login_layout"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:padding="20dp"
        android:orientation="vertical">

    <ImageView
            android:id="@+id/header_icon"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:src="@drawable/ic_account_black"/>
    
    <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="20dp"
            android:textStyle="bold"
            android:textColor="#000000"
            android:text="Bong Can Do"/>

    <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="15dp"
            android:textColor="#EB554C4C"
            android:text="tistory.bongcando.com"/>
</LinearLayout>

/res/layout/main_drawer_header.xml

 

4. 메인 영역

Navigation Drawer를 사용할 메인 레이아웃을 구성한다.

메인 레이아웃을 구성할 때에는 화면의 최상단에 위치하며 Navigation Drawer를 open시키는 버튼이나 다른 기능들을 집어넣을 수 있는 영역인 툴바(Tool bar) 영역을 먼저 구성해 준다.

 

툴바(Tool bar) 영역

먼저, 툴바 xml을 만들어준다.

<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.Toolbar
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/main_layout_toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
/>

/res/layout/main_toolbar.xml

 

그 다음 툴바를 포함한 메인 메뉴의 view를 보여줄 xml을 만들어준다.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/login_layout"
        android:layout_width="match_parent"
        android:layout_height = "match_parent"
        android:orientation="vertical">

    <!-- 툴바 -->
    <include layout="@layout/activitiy_main_toolbar"/>

    <!-- 메인 view -->
    <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="메인 레이아웃"
            android:textColor="@color/colorBlack"/>
</LinearLayout>

/res/layout/main_layout.xml

 

위의 xml과 Drawer영역 모두 포함하여 보여줄 xml을 만들어준다.

<?xml version="1.0" encoding="utf-8"?>
<!--최상위 레이아웃. DrawerLayout-->
<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/main_drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true">

    <!--메인 레이아웃-->
    <include
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        layout="@layout/activity_main_layout" />

    <!--자식 레이아웃. Navigation Drawer-->
    <com.google.android.material.navigation.NavigationView
            android:id="@+id/main_navigationView"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="left"
            app:headerLayout="@layout/activity_main_drawer_header"
            app:menu="@menu/activity_main_drawer_menu">

    </com.google.android.material.navigation.NavigationView>
</androidx.drawerlayout.widget.DrawerLayout>

/res/layout/main.xml

 

메인 레이아웃은 DrawerLayout이어야 하며 DrawerLayout의 자식 레이아웃으로 (3)에서 만든 레이아웃을 설정해준다.

이 때, 자식 레이아웃들의 배치 순서는 Main 화면에 배치되는 View가 가장 먼저오며 (3)에서 만든 header_layout, navigation_layout순으로 배치해야 한다.

 

  • andorid:fitsSystemWindows : 뷰가 차지할 수 있는 영역을 상태바, 소프트키를 제외한 영역까지 확장해 준다.

5-1. Main Activity - ActionBar

먼저, onCreate()메서드에 setSupportActionBar() 메서드를 사용해서 툴바를 액티비티의 앱바로 지정하고 setDisplayHomeAsUpEnabled() 메서드로 드로어를 꺼낼 홈버튼을 활성화 한다.

홈버튼의 이미지를 변경하고 싶다면 setHomeAsUpIndicator() 메서드를 사용한다.

만약 툴바 타이틀에 어플리케이션 이름이 나타난다면 setDisplayShowTitleEnabled() 메서드로 안보이게 가릴 수 있다.

setSupportActionBar(main_toolbar) // 툴바를 액티비티의 앱바로 지정
supportActionBar?.setDisplayHomeAsUpEnabled(true) // 드로어를 꺼낼 홈 버튼 활성화
supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_menu_white) // 홈버튼 이미지 변경
supportActionBar?.setDisplayShowTitleEnabled(false) // 툴바에 타이틀 안보이게

그 다음 onOptionsItemSelected() 메서드를 Override 한다.

override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when(item.itemId){
            android.R.id.home->{ // 메뉴 버튼
                main_drawer_layout.openDrawer(GravityCompat.START)    // 네비게이션 드로어 열기
            }
        }
        return super.onOptionsItemSelected(item)
}

 

5-2. Main Activity - NavigationItem

먼저, Activity에 NavigationView.OnNavigationItemSelectedListener를 implement시킨다.

그 다음 onCreate()메서드에 navigationItem 리스터를 만들어준다.

main_navigationView.setNavigationItemSelectedListener(this) //navigation 리스너

그 다음 onNavigationItemSelected() 메서드를 Override 한다.

override fun onNavigationItemSelected(item: MenuItem): Boolean {
        when(item.itemId){
            R.id.account-> Toast.makeText(this,"account clicked",Toast.LENGTH_SHORT).show()
            R.id.item2-> Toast.makeText(this,"item2 clicked",Toast.LENGTH_SHORT).show()
            R.id.item3-> Toast.makeText(this,"item3 clicked",Toast.LENGTH_SHORT).show()
        }
        return false
}

위의 예제는 Navigation에서 각 아이템이 클릭되었을때 어떤 아이템을 선택했는지 Toast를 띄우게 구현하였다.

 

5-3. Main Activity - Back Button

지금까지 구현된 상태로는 드로어가 나와있어도 뒤로가기 버튼을 누를시 앱이 종료되어 버린다.

드로어가 나와있을때 뒤로가기 버튼에 대한 이벤트를 처리해준다.

override fun onBackPressed() { //뒤로가기 처리
        if(main_drawer_layout.isDrawerOpen(GravityCompat.START)){
            main_drawer_layout.closeDrawers()
            // 테스트를 위해 뒤로가기 버튼시 Toast 메시지
            Toast.makeText(this,"back btn clicked",Toast.LENGTH_SHORT).show()
        } else{
            super.onBackPressed()
        }
}

 

이제 실행해보면 스와이프, 버튼으로 드로어를 꺼낼 수 있고 뒤로가기 버튼으로 드로어를 닫을 수 있다.

또한, Navigation의 각 item클릭시 어떤 item을 클릭했는지 Toast메시지가 나오게 된다.

 

Navigation Drawer

 

 

 

 


참고자료

DrawerLayout 공식문서

 

NavigationView 공식문서

 

 

 

320x100