[Android] Navigation Drawer 안에서 ExpandableListView 사용하기
Android

[Android] Navigation Drawer 안에서 ExpandableListView 사용하기

728x90

 

 

ExpandableListView란?

 ExpandableListView는 ListView의 한 종류로써, ListView는 단순히 리스트를 뿌려주는 역할을 하고, ExpandableListView는 말 그대로 확장 기능을 제공한다. 단순히 ListView처럼 array.xml을 생성하여 리소스 아이디를 직접 적용할 순 없고, HashMap을 만들어서 적용해야 한다.

 이번 실습에선 Navigation Drawer 안에서 ExpandableListView를 사용해 부모와 자식 관계를 나타내 줄 것이다. 최종 결과물로 아래 사진처럼 나오게 된다. 

 

 

코드

MainActivity.kt

 MainActivity에서는 ExpandableListView의 어댑터를 설정해야 한다. 어댑터에는 부모와 자식 관계를 나타내는 List 정보가 필요하다. childList는 MutableMap으로 구현해도 됐지만, 예제가 간단하기 때문에 MutableList를 사용했다. 

 어댑터에 부모와 자식 관계를 설정하면 Click Listener를 설정해서 클릭 이벤트를 처리해줄 수 있다. 예를 들어, 액티비티 내의 프레그먼트 전환 같은 경우를 Listener 안에서 설정 가능하다.

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        setExpandableList()
    }

    /* ExpandableListView 설정 */
    private fun setExpandableList() {
        val parentList = mutableListOf("부모 1", "부모 2", "부모 3")
        val childList = mutableListOf(
            mutableListOf(),
            mutableListOf("자식 1", "자식 2"),
            mutableListOf("자식 1", "자식 2", "자식 3")
        )

        val expandableAdapter = ExpandableListAdapter(this, parentList, childList)
        el_menu.setAdapter(expandableAdapter)

        el_menu.setOnGroupClickListener { parent, v, groupPosition, id ->
            /* todo : parent 클릭 이벤트 설정 */
            false
        }
        el_menu.setOnChildClickListener { parent, v, groupPosition, childPosition, id ->
            /* todo : child 클릭 이벤트 설정 */
            false
        }
    }
}

 

 

ExpandableListAdapter.kt

 ExpandableListAdapter에는 ExpandableListView 안에 들어가는 내용물들이 담겨있다.

 오버라이드 된 여러 개의 함수가 있지만, 그중 getGroupView와 getChildView에서 레이아웃에 뷰를 그려주게 된다. 부모 위치에 아이콘을 넣어주기 위해서 setIcon, setArrow 메소드를 별도로 작성해 사용했다.

class ExpandableListAdapter(
    private val context: Context,
    private val parents: MutableList<String>,
    private val childList: MutableList<MutableList<String>>
) : BaseExpandableListAdapter() {

    override fun getGroupCount() = parents.size

    override fun getChildrenCount(parent: Int) = childList[parent].size

    override fun getGroup(parent: Int) = parents[parent]

    override fun getChild(parent: Int, child: Int): String = childList[parent][child]

    override fun getGroupId(parent: Int) = parent.toLong()

    override fun getChildId(parent: Int, child: Int) = child.toLong()

    override fun hasStableIds() = false

    override fun isChildSelectable(groupPosition: Int, childPosition: Int) = true

    /* 부모 계층 레이아웃 설정 */
    override fun getGroupView(
        parent: Int,
        isExpanded: Boolean,
        convertView: View?,
        parentview: ViewGroup
    ): View {
        val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
        val parentView = inflater.inflate(R.layout.menu_parent, parentview, false)

        parentView.tv_list_title.text = parents[parent]

        setIcon(parent, parentView)
        setArrow(parent, parentView, isExpanded)

        return parentView
    }

    /* 자식 계층 레이아웃 설정 */
    override fun getChildView(
        parent: Int,
        child: Int,
        isLastChild: Boolean,
        convertView: View?,
        parentview: ViewGroup
    ): View {
        val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
        val childView = inflater.inflate(R.layout.menu_child, parentview, false)

        childView.tv_child_title.text = getChild(parent, child)

        return childView
    }

    /* drawer 아이콘 설정 */
    private fun setIcon(parentPosition: Int, parentView: View) {
        when (parentPosition) {
            0 -> parentView.iv_img.setImageResource(R.drawable.ic_person)
            1 -> parentView.iv_img.setImageResource(R.drawable.ic_assignment)
            2 -> parentView.iv_img.setImageResource(R.drawable.ic_settings)
        }
    }

    /* 닫힘, 열림 표시해주는 화살표 설정 */
    private fun setArrow(parentPosition: Int, parentView: View, isExpanded: Boolean) {

        /* 0번째 부모는 자식이 없으므로 화살표 설정해주지 않음 */
        if (parentPosition != 0) {
            if (isExpanded) parentView.iv_arrow_drop.setImageResource(R.drawable.ic_arrow_drop_up)
            else parentView.iv_arrow_drop.setImageResource(R.drawable.ic_arrow_drop_down)
        }
    }
}

 

activity_main.xml

 NavigationView 안에 ExpandableListView를 선언해준다. 여기서 ExpandableListView의 속성이 몇 가지 있다.

 groupIndicator 속성을 따로 설정해주지 않으면 머티리얼에서 제공하는 기본적인 뷰를 사용할 수 있다. 실습에서는 화살표를 따로 설정해주기 위해서 @null 값을 주었다.

 divider 속성을 사용하면 각 메뉴를 구분 짓는 선을 설정할 수 있다.

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

    <com.google.android.material.navigation.NavigationView
        android:id="@+id/nv_drawer"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true">

        <ExpandableListView
            android:id="@+id/el_menu"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:groupIndicator="@null" />

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

</androidx.drawerlayout.widget.DrawerLayout>

 

menu_parent.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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <ImageView
        android:id="@+id/iv_img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="16dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:ignore="ContentDescription" />

    <TextView
        android:id="@+id/tv_list_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="16dp"
        android:textSize="16sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toEndOf="@id/iv_img"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/iv_arrow_drop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="16dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

menu_child.xml

 자식의 레이아웃을 설정한다.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/tv_child_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="60dp"
        android:padding="10dp"
        android:textSize="16sp" />

</LinearLayout>

 

728x90