안드로이드 네비게이션 드로어(Navigation Drawer) 만들기. (How to make an Android Navigation Drawer)

2017. 10. 24. 18:03


1. 안드로이드 네비게이션 드로어 (Android Navigation Drawer)

네비게이션 드로어(Navigation Drawer)는 "항해"" 또는 "탐색"을 의미하는 Navigation이라는 단어와 "서랍"이라는 뜻을 가진 Drawer가 조합된 용어로써, 앱의 최상위 기본 탐색 메뉴로 동작하는 사용자 인터페이스 화면을 말합니다.

네비게이션 드로어(Navigation Drawer) 인터페이스


안드로이드 개발 가이드 홈페이지에서는 네비게이션 드로어의 역할에 촛점을 두어 "탐색 창"이라는 용어로 번역을 하고 있지만, 네비게이션 드로어가 동작하는 방식을 보면, 사전적 의미 그대로, "탐색 서랍"이 좀 더 직관적인 번역이 될 듯 합니다. (하지만 "탐색 서랍"은...음, 뭔가 어색하고 촌스런 이름이긴 하네요.)


네비게이션 드로어(Navigation Drawer)는 앱에서 제공하는 다양한 기능을 탐색할 수 있도록 도와주는 메인 메뉴(Main Menu)와 같은 역할을 수행합니다. 기본적으로 화면의 왼쪽 가장자리에 숨겨져 있는데, 화면 왼쪽 가장자리에서 오른쪽 방향으로 쓸어넘기는 스와이프(Swipe) 동작을 수행하거나, "액션 바(Action Bar)"의 탐색 아이콘(Navigation Icon)을 터치하여 화면에 나타나게 만들 수 있습니다.

네비게이션 드로어(Navigation Drawer) 동작


네비게이션 드로어(Navigation Drawer)가 제공하는 탐색 기능과 화면의 왼쪽 경계를 기준으로 열리고 닫히는 특수한 동작으로 인해, "네비게이션 드로어"를 마치 (NavigationDrawer라는 이름으로 만들어진) 하나의 뷰 위젯 클래스인 것으로 오해할 수 있습니다. 하지만 네이게이션 드로어(Navigation Drawer)는 하나의 뷰 위젯 클래스가 아니라, 머티리얼 디자인의 "디자인 패턴" 중 하나입니다. (네비게이션 드로어 디자인 패턴에 대한 내용은 "https://material.io/guidelines/patterns/navigation-drawer.html" 에서 확인할 수 있습니다.)


네비게이션 드로어(Navigation Drawer)가 단순히 뷰 위젯으로 존재하지 않는다면, 어떤 방법으로 만들 수 있는 것일까요? 정답은 바로, 화면의 가장자리에서 뷰(View)가 열리고 닫히는 기능을 만들 때 사용하는 레이아웃인 드로어레이아웃(DrawerLayout)을 사용하는 것입니다.

1.1 네비게이션 드로어(Navigation Drawer)와 드로어레이아웃(DrawerLayout)

[안드로이드 드로어레이아웃]에서 설명했듯이, 드로어레이아웃(DrawerLayout)은 자신의 영역 안에서 자식 뷰(View)가 "Drawer(서랍)"와 같은 동작을 수행하도록 만들어주는 레이아웃입니다. 앞서 설명한 네비게이션 드로어(Navigation Drawer)의 동작 방식과 정확히 일치하죠. 그러므로 네이베이션 드로어(Navigation Drawer)를 만들 때는 DrawerLayout을 액티비티의 루트 레이아웃으로 사용합니다.


그리고 DrawerLayout의 Drawer에는 탐색 항목이 나열됩니다. 탐색 항목을 표시할 뷰는 앱의 컨텐츠에 따라 다양한 구성이 가능하지만, 일반적으로 리스트뷰(ListView) 또는 리사이클러뷰(RecyclerView)를 사용합니다.


또한 DrawerLayout의 주 화면으로는 FrameLayout을 주로 사용하는데, 이는 FrameLayout이 액자(Frame)처럼 화면에 표시될 뷰(View)를 바꿔가면서 표시하기가 용이하기 때문입니다. 특히, FrameLayout이 프래그먼트(Fragment)의 컨테이너 역할을 수행하므로, Drawer의 탐색 항목 선택 시 주 화면에 표시될 내용을, 프래그먼트를 사용하여 변경하기가 쉽다는 장점이 있죠.

네비게이션 드로어(Navigation Drawer) 기본 레이아웃


2. 네비게이션 드로어(Navigation Drawer) 만들기.

자, 그럼 이제, 예제를 통해 네비게이션 드로어(Navigation Drawer)를 만드는 방법에 대해 알아보겠습니다. 예제는 아래와 같은 레이아웃으로 구성됩니다.

DrawerLayout 예제 레이아웃


네비게이션 드로어(Navigation Drawer)에 대한 쉬운 이해를 위해, 예제에서는 DrawerLayout의 주 화면으로 텍스트뷰(TextView)를 사용합니다.

2.1 MainActivity의 레이아웃 작성하기.

가장 먼저, MainActivity의 레이아웃 리소스 XML을 작성합니다. (참고로, 본문의 프로젝트는 안드로이드 스튜디오에서 프로젝트 생성 시, 액티비티 선택 단계에서 "Basic Activity"를 선택하였습니다.)

[STEP-1] "content_main.xml" - MainActivity 레이아웃 리소스 XML 파일
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.recipes4dev.examples.navigationdrawerexample.MainActivity"
    tools:showIn="@layout/activity_main">

    <android.support.v4.widget.DrawerLayout
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:id="@+id/drawer">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:textSize="48sp"
            android:text="CONTENTS"
            android:background="#FFFFFF"
            android:textColor="#000000"
            android:id="@+id/drawer_content"/>

        <ListView
            android:layout_width="200dp"
            android:layout_height="match_parent"
            android:layout_gravity="left"
            android:background="#FFC107"
            android:id="@+id/drawer_menulist" />

    </android.support.v4.widget.DrawerLayout>

</android.support.constraint.ConstraintLayout>

레이아웃 소스 코드에서 가장 먼저 눈여겨 봐야할 부분은 DrawerLayout에 추가되는 자식(Children)들의 순서입니다. 기본적으로, 주 화면으로 표시될 뷰를 첫 번째 자식(Child)으로 선언하고, Drawer로 사용될 뷰를 마지막 자식(Child)으로 선언합니다. 예제에서는 ListView가 Drawer로 사용될 것이므로, 레이아웃의 가장 마지막에 추가하였습니다.


그 다음, Drawer로 사용되는 ListView에 대해 layout_gravity 속성을 사용하여 Drawer가 어느 방향에서 나타날 것인지를 결정해야 합니다. left(start) 또는 right(end) 값을 지정할 수 있으며, 네비게이션 드로어 패턴에서는 기본적으로 left(start)를 사용합니다.


마지막으로 Drawer의 layout_width와 layout_height를 적절한 값으로 지정해야 합니다. 보통 layout_width는 부모인 DrawerLayout의 너비보다 작은 상수 값을 사용하며, layout_height는 부모의 높이에 딱 맞게 match_parent 값을 지정합니다.


각 항목에 대한 좀 더 자세한 설명은 [안드로이드 드로어레이아웃]의 내용을 참고하시기 바랍니다.

2.2 Drawer의 리스트뷰 초기화.

다음 단계는 Drawer에 표시될 리스트뷰를 초기화하는 것입니다.

[STEP-2] "MainActivity.java" - onCreate() 함수에서 리스트뷰 초기화.
public class MainActivity extends AppCompatActivity {

    ListView listview = null ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        // ... 코드 계속

        final String[] items = {"WHITE", "RED", "GREEN", "BLUE", "BLACK"} ;
        ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, items) ;

        listview = (ListView) findViewById(R.id.drawer_menulist) ;
        listview.setAdapter(adapter) ;

        // 코드 계속 ...
    }
}

2.3 리스트뷰 아이템 클릭 이벤트 처리

이제 Drawer에 표시된 리스트뷰의 아이템을 선택하면, 주 화면의 컨텐츠를 변경하는 코드를 작성합니다. 예제에서는 컨텐츠로 하나의 텍스트뷰만을 사용하며, 아이템 선택 시 텍스트뷰의 텍스트, 배경색, 글자색을 변경하도록 만듭니다.

[STEP-3] "MainActivity.java" - 리스트뷰 아이템 클릭 이벤트 처리.
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        // ... 코드 계속

        listview.setOnItemClickListener(new ListView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView parent, View v, int position, long id) {

                TextView contentTextview = (TextView) findViewById(R.id.drawer_content) ;

                switch (position) {
                    case 0 : // WHITE
                        contentTextview.setBackgroundColor(Color.rgb(0xFF, 0xFF, 0xFF)) ;
                        contentTextview.setTextColor(Color.rgb(0x00, 0x00, 0x00)) ;
                        contentTextview.setText("WHITE") ;
                        break ;
                    case 1 : // RED
                        contentTextview.setBackgroundColor(Color.rgb(0xFF, 0x00, 0x00)) ;
                        contentTextview.setTextColor(Color.rgb(0xFF, 0xFF, 0xFF)) ;
                        contentTextview.setText("RED") ;
                        break ;
                    case 2 : // GREEN
                        contentTextview.setBackgroundColor(Color.rgb(0x00, 0xFF, 0x00)) ;
                        contentTextview.setTextColor(Color.rgb(0x00, 0x00, 0x00)) ;
                        contentTextview.setText("GREEN") ;
                        break ;
                    case 3 : // BLUE
                        contentTextview.setBackgroundColor(Color.rgb(0x00, 0x00, 0xFF)) ;
                        contentTextview.setTextColor(Color.rgb(0xFF, 0xFF, 0xFF)) ;
                        contentTextview.setText("BLUE") ;
                        break ;
                    case 4 : // BLACK
                        contentTextview.setBackgroundColor(Color.rgb(0x00, 0x00, 0x00)) ;
                        contentTextview.setTextColor(Color.rgb(0xFF, 0xFF, 0xFF)) ;
                        contentTextview.setText("BLACK") ;
                        break ;
                }

                // 코드 계속 ...
            }
        });
    }
}

2.4 리스트뷰의 아이템을 선택하면 Drawer 닫기.

마지막으로, Drawer의 리스트뷰에서 아이템을 선택하고 나면, Drawer가 닫히도록 만듭니다. 열려 있는 Drawer를 닫기 위해서는 DrawerLayout의 closeDrawer() 함수를 호출하면 됩니다.

[STEP-4] "MainActivity.java" - DrawerLayout의 Drawer 닫기.

public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { // ... 코드 계속 listview.setOnItemClickListener(new ListView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View v, int position, long id) { // ... 코드 계속 // close drawer. DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer) ; drawer.closeDrawer(Gravity.LEFT) ; } }); } }

3. 예제 실행

예제 코드를 작성하고 실행하면, 아래 그림과 같은 화면이 표시됩니다.

네비게이션 드로어(Navigation Drawer) 예제 실행 화면


실행된 초기 화면만 보면 아무런 기능이 없는 것처럼 보이지만, 화면 왼쪽 가장자리를 스와이프하면 리스트뷰가 표시된 Drawer가 나타나는 것을 확인할 수 있습니다. 그리고 리스트뷰의 아이템을 선택하면, 선택된 아이템에 따라 주 화면의 색상과 텍스트가 변경되는 것을 확인할 수 있습니다.

4. 참고

.END.


ANDROID 프로그래밍/NAVIGATION , , , , , , , , ,

  1. Blog Icon
    초보요옹

    항상 잘 보며 공부 열심히 하고 있는 대학생입니다.(감사합니다ㅠㅠ)
    처음으로 예제와 설명을 보며 막혀서ㅠㅠ
    처음 MainActivity XML부터 막혀버렸습니다 ㅠㅠ 프로젝트 생성시 네비게이션드로어로 생성하여 적용도 해봤고, 그냥 일반 화면으로 만든 프로젝트에서도 적용을 해봤으나 app빨간글씨로 계속 떠있어서 이렇게 질문드립니다 ㅠㅠ

  2. 친절하지 못한 본문의 내용 때문에 예제 작성 단계부터 혼란을 드렸네요.
    일단 본문의 내용에서 메인액티비티의 레이아웃 XML 소스를 수정했습니다.

    본문의 예제는 프로젝트 생성 시, "Basic Activity"를 선택했습니다.
    그리고 "content_main.xml" 파일안에 DrawerLayout이 포함된 메인액티비티의 레이아웃을 작성하였습니다.
    참고바랍니다.

    다시 한번, 혼란을 드려 죄송합니다.

    감사합니다.

  3. Blog Icon
    초보요옹

    아닙니다! 항상 정리 잘 된 좋은 자료들을 보며 덕분에 편하게 공부를 하고 있습니다 ㅎㅎ
    바쁘실텐데 답변해주셔서 감사합니다. 좋은 하루 되세요~

  4. 열심히 정리하였지만, 잘 정리한 것인지는 모르겠습니다.
    조금 더 시간을 할애할 수 있다면 좋을텐데.. 회사일이 만만치않게 바빠져서 쉽게 시간을 낼 수가 없네요.
    (변명..변명..)

    "초보요옹"님도 좋은 하루 보내시길 바랍니다.

    감사합니다.

  5. Blog Icon
    힐러

    안녕하세요 전체소스코드를 볼수있을까요?? 직접만들어보려는데 중략부분때문에 전체를 보고싶어서요...

  6. 전체 소스 코드는 본문에 모두 작성되어 있습니다. 중략 부분은 없습니다.

    아마 "/// 코드 계속 ..." 이라는 곳 때문에 착각하신 듯 하네요.
    "/// 코드 계속 ..." 이라는 주석은 앞 또는 뒤 단계에서 작성되었거나 작성될 것이다는 의미이지 생략의 의미가 아닙니다.
    각 단계 별로 작성되어야 할 내용들을 강조하기 위해 이전 단계 또는 다음 단계에서 작성되는 코드가 있다는 표시의 의미이죠.

    본문에서 작성하는 내용대로 코드를 작성하시면 예제 결과와 동일한 화면과 동작을 확인하실 수 있습니다.

    작성해보시고 추가적인 질문 내용이 있으면 질문글 남겨주세요.

    감사합니다.

  7. Blog Icon
    감사합니다

    이 내용 관련하여 자료가 많이 없었는데 뽀따님 게시글 보고 해결할 수 있었습니다.
    친절한 설명, 그리고 답글 감사합니다 ㅜㅜ 감덩입니다. 온라인에서도 이런 분이 계시다니요ㅜㅜ

  8. 원하는 자료를 쉽게 찾으실 수 없었나보네요.
    제가 정리한 글이 도움이 되었다니, 뿌듯합니다.
    더 많은 도움 드릴 수 있도록 노력하겠습니다.

    감사합니다.

  9. Blog Icon
    GloryKime

    @string/appbar_scrolling_view_behavior 이부분에서 에러가 나는데 풀소스를 올려주실순없나요? ㅠㅠ 정보는 감사합니다.

  10. 에러가 나는 부분은 동작에 큰 영향을 주지 않을 것 같습니다. 지우고 하셔도 될 것 같습니다.

    참고로, 본문의 소스 내용 중에서 Bold 체로 작성된 소스만 직접 작성시면 됩니다. 그 외 코드는 스튜디오에서 생성해주는 코드입니다.

    그리고 죄송하지만 풀소스는 올리지 않습니다. 파일 관리가 번거롭기도 하지만, 이 글을 읽으시는 분들이 직접 코드를 작성하면서 학습하시길 바라기 때문입니다.

    풀 소스를 올리면 한번 실행해버리고마는 경우를 많이 봐서 말이죠.

    죄송하고, 감사합니다.

  11. Blog Icon
    윤하나

    안녕하세요 열심히 글 정독하면서 공부하고 있는 대학생입니다!

    스와이프 동작과 탐색 아이콘을 동시에 같이 사용하고 싶은데

    여기 소스를 그대로 한번 따라해본 결과 스와이프는 되는데 탐색아이콘은 어떻게 생성해야하는지 감이 잘 안잡히더라구요 ㅜㅜ

    content_main.xml에서 탐색 아이콘을 생성하는게 아니라
    MainActivity.java 에서 탐색 아이콘을 따로 만들어줘야하는지 알려주실 수 있으신가요?

  12. 탐색 아이콘이라면 앱바(AppBar)의 왼쪽에 표시되는 버튼(▤ <- 이런 모양)을 말씀하시는 거죠?

    네비게이션드로어를 열고 닫기 위해서 사용하실거라 생각이 드는데요. 일반적으로 개발자가 직접 만들지 않고, 툴바(Toolbar)를 통해 제공되는 기능을 이용합니다.

    제가 직접 예제를 통해 설명을 드리면 좋을텐데, 최근에 업무가 많아서 글을 작성할 시간이 부족한 상태라 힘들 것 같네요.

    일단, 안드로이드 스튜디오에서 앱 프로젝트 생성할 때 "Navigation Drawer Activity"를 선택해서, 생성되는 코드를 분석해 보세요. 그 중에 ActionBarDrawerToggle를 찾아보시면 됩니다.

    아니면 ActionBarDrawerToggle 을 구글링 하시면 많은 자료 찾으실 수 있을거라 생각합니다.

    직접 도움을 못드려 죄송합니다.

    감사합니다.

  13. Blog Icon
    늘_감사합니다

    많은 도움을 얻어가는 한 대학생입니다! 다름이 아니라 네비게이션 엑티비티( 하단 바)랑 드로어랑 다른 것으로 아는데, 혹시 하단 바에 대한 포스팅이 있을까요??

  14. 하단 바.. 라고 하시면 "시스템 네비게이션 바"를 말씀하시는 건가요?

    그렇다면 따로 정리한 글은 없는데요. 혹시 어떤 내용이 궁금하신가요?

  15. Blog Icon
    잘 보고 있습니다

    스와이프로 드로어가 들어왔다 나갔다 하는 부분은 기본적으로 xml 속 drawerlayout에 들어있는건가요? 현재 너무 왼쪽부분에서만 드래그가 되어 드래그 인식 범위를 키우고 싶어서요 ㅠㅠ

  16. 음.. 질문하신 내용에 대해서는 저도 한번도 고민하거나 다루어 본 적이 없어서, 경험에 의한 해답을 드리기가 힘들 것 같습니다.

    하지만 스택오버플로우에 아래와 같은 질문과 답변이 있으니, 한번 시도해보시는 것도 괜찮을 것 같습니다.

    https://stackoverflow.com/questions/22005019/android-navigation-drawer-trigger-area-is-too-small

    도움되시길 바랍니다.

    감사합니다.

  17. Blog Icon
    안드슬레이브

    Navigation Drawer 관련해서 새로 강의를 수정하실 계획같은건 없으신가요..?
    2020년도 들어 NavigationDrawer이 업데이트 됐는지 액티비티로 Navgation Drawer를 만들면
    기존의 글들과 달리 NaviController 라든지 NavigationUI 등등 할수 없는 객체들고가 함수들이 추가되어서 뭐가뭔지 자료가 부족해 이해하기가 힘드네요 ㅠ

  18. 에고... 저도 Navigation Drawer를 새로 변경된 내용으로 업데이트하고 싶은데, 여유가 없네요.
    지금 하고 있는 회사일이 너무너무 바쁘기도 하고, 안드로이드는 당분간 건드릴 일이 없어서요.

    한번 훓어보니, 안드로이드 공식 홈페이지나, 다른 블로그 글에서 어렵지 않게 설명된 것 같아요. 너무 어렵게 생각하지 마시고, 차근차근 접근해보시면 쉽게 하실 수 있을거라 생각합니다.

    감사합니다.

  19. Blog Icon
    has33

    뽀따님짱 전 봐도 모르겠어요 제가 글을 읽고있는건지 글자를 읽고 있는건지 쫘악 정독하는데 공부가힘드네여

  20. 설명을 조금 더 쉽고 친절하게 했어야 하는데..
    내용이 좀 어렵게 정리가 되어있죠? ㅜㅜ

    공부하시다가 잘 이해되지 않는 내용은 언제든 질문글 남겨주세요.

    최대한 도움드릴 수 있도록 노력하겠습니다.

    감사합니다.