[kotlin] Navigation Drawer 네비게이션 커스텀 완벽정리 feat. Expandable List 커스텀

2021. 11. 26. 21:40개발을 파헤치다/Android

반응형

Navigation Drawer는 정말 많은 앱 서비스에서 활용되는 레이아웃입니다. 흔히 사이드 메뉴라고도 하죠.

막상 적용하려고 보면 은근히 손이 가는 게 많은데요. 커스텀을 하게 될 경우 좀 많이 어려워질 수 있습니다.

오늘은 Navigation Drawer에 흔한 메뉴 말고 내용이 변하는 카테고리(이중 리스트...)를 커스텀으로 적용해 볼 예정입니다. 

구글에 찾아도 절대 잘 나오지 않는 내용이니 끝까지 꼭 읽어보시고 팁 얻어가시길 바라겠습니다.

 

Drawer Layout 적용

가장 먼저 액티비티 레이아웃의 가장 바깥 쪽에 Drawer Layout을 적용해주어야 합니다.
Drawer Layout은 네비게이션 아이콘을 클릭하거나 사이드를 드래그했을 기존 레이아웃 위에 새로운 메뉴를 보여줍니다.

말 그대로 서랍처럼 열고 닫을 수 있게 만들어주는 역할이라고 생각하면 됩니다.

Drawer Layout에 보이는 내용이 바로 Navigation View입니다. 이곳에 원하는 메뉴나 헤더를 구성할 수 있습니다. Drawer Layout과 Navigation View는 세트처럼 움직이는데요.
구현해야 할 때 주의해야 할 점이 있습니다. 바로 View 레벨에 관한 문제입니다. 특히, Constraint Layout에서 Drawer Layout과 Navigation View 사용 시 나타날 수 있는 문제입니다.

<DrawerLayout>

    ...

    <ConstraintLayout>

    ...

      <NavigationView/>

    </ConstraintLayout>
</DrawerLayout>

이렇게 레이아웃 구성을 짜게 되면 에러가 발생합니다. Drawer Layout 바로 아래에 Navigation View가 위치해야 합니다. 이 경우 아래와 같은 에러가 발생합니다.

"java.lang.IllegalArgumentException No drawer view found with gravity RIGHT(or Left)"
<DrawerLayout>

    ...
    
    <NavigationView/>
     
    <ConstraintLayout>
    
    ...
    
    </ConstraintLayout>
</DrawerLayout>

다음은 이렇게 구성하는 경우인데요. 여기서도 문제가 발생합니다.
Drawer를 열면 바로 닫혀버린다거나 Navigation View의 메뉴가 클릭되지 않는 현상이 발생합니다. 이러한 문제가 발생하는 이유는 레이아웃이 렌더링 되는 순서와 관련이 있는데요.
같은 View 레벨이라면 나중에 정의된 레이아웃이 더 상위에 존재합니다. 그러니까 Navigation View는 메인 레이아웃보다 더 상위에 있어야 하겠죠? 왜냐하면 사이드 메뉴가 오픈되었을 때 누르는 메뉴기 때문이죠. 이게 메인 레이아웃보다 더 먼저 정의되어있으니 가려져서 클릭이 되지 않았던 것입니다.

<DrawerLayout>

    ...
     
    <ConstraintLayout>
        <!-- Main Layout -->
    
    ...
    
    </ConstraintLayout>
    <NavigationView/>
</DrawerLayout>

따라서 Drawer Layout을 구현할 때에는 위의 구조를 반드시 지켜서 구현합니다.

<?xml version="1.0" encoding="utf-8"?>
<layout
    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"
    >
    <!-- Navigation Drawer Layout -->
    <androidx.drawerlayout.widget.DrawerLayout
        android:id="@+id/main_drawer_layout"
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        tools:openDrawer="start">


        <!-- 메인 Activity Layout -->
        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context="com.leoncorp.jejuand.ui.main.MainActivity">

            <!-- 커스텀 툴바 -->
            <include
                android:id="@+id/main_tool_bar"
                layout="@layout/main_tool_bar"
                />

           <!-- 메인 액티비티 레이아웃 -->

    </androidx.drawerlayout.widget.DrawerLayout>
</layout>

Navigation View를 정의하기 전에 먼저 메인 레이아웃 최상단에 Drawer Layout부터 정의합니다.

Navigation Header 레이아웃 생성

Navigation View는 아래 두 가지로 구성된다고 볼 수 있습니다.

  • header: 상단에 위치하는 레이아웃. 프로필 이미지나 여러 가지 데이터가 올 수 있습니다.
  • menu: 리스트 형태로 메뉴가 나타납니다.


그중 먼저 header를 구현해봅시다.

<?xml version="1.0" encoding="utf-8"?>

    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="@dimen/nav_main_header_height"
    android:background="@color/scarlet"
    android:gravity="center"
    android:orientation="vertical"
    android:theme="@style/ThemeOverlay.AppCompat.Dark">

    <!-- 로그인 -->
    <TextView
        android:id="@+id/nav_login"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:fontFamily="@font/nanum_gothic"
        android:text="@string/login_menu"
        android:textColor="#000000"
        android:textSize="16.7sp"
        android:textStyle="normal"

        app:layout_constraintBottom_toTopOf="@id/nav_mypage"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.1"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.3" />

    <!-- 홈 아이콘 -->
    <ImageView
        android:id="@+id/nav_home"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="@dimen/layout_default_margin_medium"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@id/nav_close"
        app:layout_constraintHorizontal_bias="0.8"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintStart_toEndOf="@id/nav_login"
        app:layout_constraintTop_toTopOf="parent"

        app:layout_constraintVertical_bias="0.14"
        app:srcCompat="@drawable/home_icon" />


    <!-- 닫기 아이콘 -->
    <ImageView
        android:id="@+id/nav_close"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="@dimen/layout_default_margin_medium"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/nav_home"
        app:layout_constraintTop_toTopOf="parent"

        app:layout_constraintVertical_bias="0.19"
        app:srcCompat="@drawable/nav_main_close_icon" />

    <!-- 마이페이지 아이콘 -->
    <TextView
        android:id="@+id/nav_mypage"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:drawableStart="@drawable/nav_main_my_page_icon"
        android:drawablePadding="@dimen/layout_default_margin"
        android:fontFamily="@font/nanum_gothic"
        android:lineSpacingExtra="-6sp"
        android:text="@string/mypage_menu"
        android:textColor="#000000"
        android:textSize="16sp"

        android:textStyle="normal"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="@id/nav_login"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.7" />

    <!-- 주문/배송조회 아이콘 -->
    <TextView
        android:id="@+id/nav_order"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:drawableStart="@drawable/nav_main_order_icon"
        android:drawablePadding="@dimen/layout_default_margin"
        android:fontFamily="@font/nanum_gothic"
        android:lineSpacingExtra="-6sp"
        android:text="@string/order_menu"
        android:textColor="#000000"
        android:textSize="16sp"

        android:textStyle="normal"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="@id/nav_close"
        app:layout_constraintHorizontal_bias="1.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.7" />

</androidx.constraintlayout.widget.ConstraintLayout>

res > layout > nav_header_main.xml 파일을 생성하고 위와 같이 구현했습니다.
이렇게 따로 xml 파일을 만들어서 구현하는 것이 후에 유지보수 측면에서도 좋습니다.
나중에 Navigation View에 포함만 시켜주면 되거든요.

 

Navigation Menu 레이아웃 생성

이어서 Navigation View에 나타낼 메뉴를 생성합니다.

res > menu > main_nav_menu_list.xml 파일을 생성하고 아래와 같이 구현합니다.
menu 디렉토리가 없다면 새로 생성해주어야 합니다.

?xml version="1.0" encoding="utf-8"?>

    <item
        android:id="@+id/menu_item1"
        android:title="메뉴 아이템 1" />
    <item
        android:id="@+id/menu_item2"
        android:title="메뉴 아이템 2" />
    <item
        android:id="@+id/menu_item3"
        android:title="메뉴 아이템 3" />
</menu>

 

Navigation View 생성 및 적용

<?xml version="1.0" encoding="utf-8"?>
<layout
    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"
    >
    <!-- Navigation Drawer Layout -->
    <androidx.drawerlayout.widget.DrawerLayout
        android:id="@+id/main_drawer_layout"
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        tools:openDrawer="start">


        <!-- 메인 Activity Layout -->
        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context="com.leoncorp.jejuand.ui.main.MainActivity">

            <!-- 커스텀 툴바 -->
            <include
                android:id="@+id/main_tool_bar"
                layout="@layout/main_tool_bar"
                />

           <!-- 메인 액티비티 레이아웃 -->
           
         <!-- Navigation View -->
        <com.google.android.material.navigation.NavigationView
            android:id="@+id/main_navigation_view"
            android:layout_height="match_parent"
            android:layout_width="wrap_content"
            android:layout_gravity="start"
            app:menu="@menu/main_nav_menu_list"
            app:headerLayout="@layout/nav_header_main"/>

    </androidx.drawerlayout.widget.DrawerLayout>
</layout>

이제 메인 레이아웃에 Navigation View를 적용할 차례입니다.
메인 레이아웃 태그 다음에 위와 같이 Navigation View를 정의합니다.
속성 중에 menu와 headerLayout이 있는데 여기에 앞서 정의한 Header 레이아웃과 menu 레이아웃을 설정해줍니다. 

Activity 구현

class MainActivity : BaseActivity<ActivityMainBinding, MainViewModel>
    (R.layout.activity_main), NavigationView.OnNavigationItemSelectedListener {

    override val viewModel = MainViewModel()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // 네비게이션 메뉴를 초기화
        initNavigationMenu()
    }


    // 네비게이션 메뉴를 초기화하는 메서드
    private fun initNavigationMenu(){
        val drawerLayout = binding.mainDrawerLayout
        val navView = binding.mainNavigationView


        navView.setNavigationItemSelectedListener(this)

        // 네비게이션 아이콘에 클릭 이벤트 연결
        val navMenu = binding.mainToolBar.mainToolBarNav
        navMenu.setOnClickListener {
            drawerLayout.openDrawer(GravityCompat.START)
        }

        // 네비게이션 헤더 메뉴에 클릭 이벤트 연결
        val headerView = navView.getHeaderView(0)
        // drawer 닫기 버튼
        val closeBtn = headerView.findViewById<ImageView>(R.id.nav_close)
        closeBtn.setOnClickListener {
            drawerLayout.closeDrawer(GravityCompat.START)
        }

    }



    /*
    *   메인 액션바 옵션을 설정하는 함수
    *
    * */
    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        val inflater = menuInflater
        inflater.inflate(R.menu.main_tool_bar_menu, menu)
        // 커스텀 메뉴에 대해서 클릭 이벤트를 설정한다
        val searchIcon = menu?.findItem(R.id.main_tool_bar_search)
        searchIcon?.actionView?.setOnClickListener {
            onOptionsItemSelected(searchIcon)
        }
        val cartIcon = menu?.findItem(R.id.main_tool_bar_cart)
        cartIcon?.actionView?.setOnClickListener {
            onOptionsItemSelected(cartIcon)
        }
        return super.onCreateOptionsMenu(menu)
    }

    /*
    *   메인 액션바 메뉴 선택시 이벤트 처리하는 함
    * */

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when(item.itemId){
            R.id.main_tool_bar_search -> {
                Toast.makeText(this, "검색하기", Toast.LENGTH_SHORT).show()
            }

            R.id.main_tool_bar_cart -> {
                Toast.makeText(this, "장바구니", Toast.LENGTH_SHORT).show()
            }
        }
        return super.onOptionsItemSelected(item)
    }

    /*
    *   네비게이션 메뉴 선택 시 이벤트 처리하는 함수
    * */
    override fun onNavigationItemSelected(item: MenuItem): Boolean {
        when(item.itemId){
            R.id.menu_item1 -> Toast.makeText(this, "메뉴1", Toast.LENGTH_SHORT).show()
            R.id.menu_item2 -> Toast.makeText(this, "메뉴2", Toast.LENGTH_SHORT).show()
            R.id.menu_item3 -> Toast.makeText(this, "메뉴3", Toast.LENGTH_SHORT).show()
        }
        return false
    }
}

이제 Activity에 구현할 차례입니다.
저의 경우 커스텀 Action Bar에 네비게이션 아이콘을 설정해두고 클릭 이벤트를 설정했습니다.

Navigation View에 연결된 Header를 가져오려면 아래처럼 활용하면 됩니다.

 // 네비게이션 헤더 메뉴에 클릭 이벤트 연결
 val headerView = navView.getHeaderView(0)
 

Navigation View에 있는 메뉴를 클릭할 때 기능이 동작하게 하기 위해서 Listener도 구현을 해주어야 하는데요. 이건 Activity에 NavigationView.OnNavigationItemSelectedListener 인터페이스를 추가해주는 것으로 해결할 수 있습니다. 추가 시 구현해야 하는 onNavigationItemSelected 메서드에 구현하면 됩니다.

 

Navigation Drawer 적용

 

Navigation Menu에 Expandable List 적용하기

네비게이션 메뉴가 고정되어있다면 좋겠지만 서비스 기획에 따라 변하는 값이 포함되는 경우도 있습니다.

이럴 경우 Navigation View를 커스텀으로 만들어줘야 하는데요.

이번에는 Navigation View의 메뉴 부분에 Expandable List를 적용하는 방법을 알아보도록 하겠습니다.

Navigation View 레이아웃 구현

<?xml version="1.0" encoding="utf-8"?>
<layout
    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"
    >
    <!-- Navigation Drawer Layout -->
    <androidx.drawerlayout.widget.DrawerLayout
        android:id="@+id/main_drawer_layout"
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        tools:openDrawer="start">


        <!-- 메인 Activity Layout -->
        ...
        
        <!-- Navigation View -->
        <com.google.android.material.navigation.NavigationView
            android:id="@+id/main_navigation_view"
            android:layout_height="match_parent"
            android:layout_width="wrap_content"
            android:layout_gravity="start"
            >

            <LinearLayout
                android:layout_height="match_parent"
                android:layout_width="match_parent"
                android:orientation="vertical">

                <!-- 네비게이션 헤더 -->
                <include
                    android:id="@+id/nav_header_main"
                    layout="@layout/nav_header_main"/>

                <!-- 카테고리 동적 입력을 위한 Expandable List View -->
                <ExpandableListView
                    android:id="@+id/nav_category_list_view"
                    android:layout_height="match_parent"
                    android:layout_width="match_parent"
                    android:groupIndicator="@null"/>

            </LinearLayout>



        </com.google.android.material.navigation.NavigationView>

    </androidx.drawerlayout.widget.DrawerLayout>
</layout>

먼저 Navigation View 레이아웃부터 살펴보겠습니다. Drawer Layout은 이미 적용되었다고 가정하겠습니다. 
앞에서는 Navigation View의 속성 값을 통해 header와 menu를 설정해줬습니다. 하지만 커스텀하려면 직접 레이아웃을 Navigation View 아래에 구현해야 합니다.이때 주의할 점은 반드시 Linear Layout을 사용하여 header와 menu 부분이 구분 지어 보일 수 있게 하는 것입니다. 이렇게 레이아웃을 설정하지 않으면 header와 menu가 겹쳐져서 나오게 됩니다.

부모 아이템 레이아웃 구현

Expandable List는 카테고리를 나타내기 위해 사용할 겁니다.
하나의 카테고리에 여러 가지 항목이 또 들어가기 때문에 이중 리스트 형식이 필요한데 Expandable List가 여기에 쓰기 딱 좋습니다.

먼저 부모 아이템을 보여줄 레이아웃을 구성합니다.
res > layout에 xml 파일을 생성합니다.

<?xml version="1.0" encoding="utf-8"?>
<layout
    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"
    >
<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="@dimen/nav_category_parent_item_height">


    <TextView
        android:id="@+id/category_parent_name"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.15"
        android:fontFamily="@font/nanum_gothic"
        android:textStyle="bold"
        android:textSize="16sp"
        android:textColor="#000000"
        android:lineSpacingExtra="-6sp"
        android:drawableStart="@drawable/nav_menu_premium_icon"
        android:drawablePadding="@dimen/layout_default_margin"
        android:text="제주앤 프리미엄"/>

</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

 

자식 아이템 레이아웃 구현

이제 제일 바깥 리스트를 눌렀을 때 하위 항목으로 보일 자식 리스트의 레이아웃을 구현합니다.

<?xml version="1.0" encoding="utf-8"?>
<layout
    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"
    >
<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="@dimen/nav_category_child_item_height"
    android:background="#eeeeee">

    <TextView
        android:id="@+id/category_child_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_bias="0.2"
        android:fontFamily="@font/nanum_gothic"
        android:textStyle="normal"
        android:textSize="15sp"
        android:textColor="#000000"
        android:lineSpacingExtra="-5sp"
        android:text="농산품"
        />

</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

 

Expandable List Adapter 구현

이제 데이터를 넣었을 때 항목들이 제대로 보일 수 있게 레이아웃에 데이터를 입혀주는 작업을 해야겠죠?

그 역할을 하는 것이 바로 Expandable List Adapter입니다.

class CategoryListAdapter(
    private val context: Context,
    private val parentList: MutableList<String>,
    private val childList: MutableList<MutableList<String>>
): BaseExpandableListAdapter() {
    override fun getGroupCount(): Int {
        return parentList.size
    }

    override fun getChildrenCount(groupPosition: Int): Int {
        return childList[groupPosition].size
    }

    override fun getGroup(groupPosition: Int): Any {
        return parentList[groupPosition]
    }

    override fun getChild(groupPosition: Int, childPosition: Int): Any {
        return childList[groupPosition][childPosition]
    }

    override fun getGroupId(groupPosition: Int): Long {
        return groupPosition.toLong()
    }

    override fun getChildId(groupPosition: Int, childPosition: Int): Long {
        return childPosition.toLong()
    }

    override fun hasStableIds(): Boolean {
        return false
    }

    /*
    *   부모 리스트 아이템 레이아웃 설정
    * */
    override fun getGroupView(
        groupPosition: Int,
        isExpanded: Boolean,
        convertView: View?,
        parent: ViewGroup?
    ): View {
        val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
        val parentView = inflater.inflate(R.layout.category_parent_list_item, parent,false)
        val parentCategory = parentView.findViewById<TextView>(R.id.category_parent_name)
        parentCategory.text = parentList[groupPosition]
        return parentView

    }

    /*
    * 자식 리스트 아이템 레이아웃 설정
    * */
    override fun getChildView(
        groupPosition: Int,
        childPosition: Int,
        isLastChild: Boolean,
        convertView: View?,
        parent: ViewGroup?
    ): View {
        val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
        val childView = inflater.inflate(R.layout.category_child_list_item, parent,false)
        val childCategory = childView.findViewById<TextView>(R.id.category_child_name)
        childCategory.text = getChild(groupPosition, childPosition) as String
        return childView
    }

    override fun isChildSelectable(groupPosition: Int, childPosition: Int): Boolean {
        return true
    }
}

BaseExpandable List Adapter를 상속하면 구현해야 할 메서드들이 나옵니다.
RecyclerView와 아주 크게 다르지 않으니 쉽게 구현할 수 있습니다.
단, Child 데이터의 경우 이중 리스트이기 때문에 index를 잘 설정해주어야 합니다.

Activity 구현

class MainActivity : BaseActivity<ActivityMainBinding, MainViewModel>
    (R.layout.activity_main){

    override val viewModel = MainViewModel()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // 네비게이션 메뉴를 초기화
        initNavigationMenu()
    }

    
    // 네비게이션 메뉴를 초기화하는 메서드
    private fun initNavigationMenu(){
        val drawerLayout = binding.mainDrawerLayout
        val navView = binding.mainNavigationView


        navView.setNavigationItemSelectedListener(this)

        // 네비게이션 아이콘에 클릭 이벤트 연결
        val navMenu = binding.mainToolBar.mainToolBarNav
        navMenu.setOnClickListener {
            drawerLayout.openDrawer(GravityCompat.START)
        }

        // 네비게이션 헤더 메뉴에 클릭 이벤트 연결
//        val headerView = navView.getHeaderView(0)
        val headerView = navView.findViewById<ConstraintLayout>(R.id.nav_header_main)

        // drawer 닫기 버튼
        val closeBtn = headerView.findViewById<ImageView>(R.id.nav_close)
        closeBtn.setOnClickListener {
            drawerLayout.closeDrawer(GravityCompat.START)
        }

        initCategoryList()
    }

    // 네비게이션 카테고리 리스트를 초기화하는 메서드
    private fun initCategoryList(){
        // Dummy Data
        val parentList = mutableListOf<String>(
            "카테고리1",
            "카테고리2",
            "카테고리3",
            "카테고리4")
        val childList = mutableListOf(
            mutableListOf("차일드1", "차일드2", "차일드3"),
            mutableListOf("차일드1"),
            mutableListOf("차일드1"),
            mutableListOf("차일드1")
        )
        
        val categoryList = binding.navCategoryListView.findViewById<ExpandableListView>(R.id.nav_category_list_view)
        // List Adapter 초기화
        val categoryListAdapter = CategoryListAdapter(this, parentList, childList)
        // List에 Adapter 연결
        categoryList.setAdapter(categoryListAdapter)
        // 부모 아이템 클릭 ㅇㅣ벤트
        categoryList.setOnGroupClickListener { parent, v, groupPosition, id ->
            false
        }
        // 자식 아이템 클릭 ㅇㅣ벤트
        categoryList.setOnChildClickListener { parent, v, groupPosition, childPosition, id ->
            false
        }
    }


    /*
    *   메인 액션바 옵션을 설정하는 함수
    *
    * */
    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        val inflater = menuInflater
        inflater.inflate(R.menu.main_tool_bar_menu, menu)
        // 커스텀 메뉴에 대해서 클릭 이벤트를 설정한다
        val searchIcon = menu?.findItem(R.id.main_tool_bar_search)
        searchIcon?.actionView?.setOnClickListener {
            onOptionsItemSelected(searchIcon)
        }
        val cartIcon = menu?.findItem(R.id.main_tool_bar_cart)
        cartIcon?.actionView?.setOnClickListener {
            onOptionsItemSelected(cartIcon)
        }
        return super.onCreateOptionsMenu(menu)
    }

    /*
    *   메인 액션바 메뉴 선택시 이벤트 처리하는 함
    * */

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when(item.itemId){
            R.id.main_tool_bar_search -> {
                Toast.makeText(this, "검색하기", Toast.LENGTH_SHORT).show()
            }

            R.id.main_tool_bar_cart -> {
                Toast.makeText(this, "장바구니", Toast.LENGTH_SHORT).show()
            }
        }
        return super.onOptionsItemSelected(item)
    }

}

네비게이션 메뉴 클릭 시 이벤트를 Expandable List의 onClickListener에서 처리하기 때문에 앞서 추가한 인터페이스 NavigationView.OnNavigationItemSelectedListener는 필요가 없어지게 됩니다. 
이렇게 Navigation Drawer에 Expandable List까지 커스텀으로 적용해보기가 완료되었습니다.

반응형