2018年5月16日 星期三

[Android] [Kotlin] Set Expandable List View in Drawer Layout


If we want to create the layout as below, we need to create the Drawer and Expandable List View.

For fulfill our purpose, we need to create some files. 




First, your main_activity layout must add drawer and content expendable list.

activity_main.xml

<android.support.v4.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/drawerLayout"    
android:layout_width="match_parent"    
android:layout_height="match_parent"    
android:fitsSystemWindows="true">
    
    <android.support.design.widget.NavigationView
        android:id="@+id/naviView"        
        android:layout_width="wrap_content"        
        android:layout_height="match_parent"        
        android:layout_gravity="start"       
        app:headerLayout="@layout/drawer_header">

        <ExpandableListView         
           android:id="@+id/navigationmenu"            
           android:layout_width="wrap_content"            
           android:layout_height="match_parent"            
           android:layout_marginTop="151dp"            
           android:background="@android:color/white">        
        </ExpandableListView>

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

Add listheader.xml for each title in drawer.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout    
    xmlns:android="http://schemas.android.com/apk/res/android"    
    android:orientation="vertical"    
    android:layout_width="match_parent"    
    android:layout_height="match_parent">    
        <LinearLayout        
            android:layout_width="match_parent"        
            android:layout_height="match_parent"        
            android:layout_marginLeft="20dp"        
            android:orientation="horizontal">
        <ImageView        
            android:id="@+id/iconimage"            
            android:layout_width="30dp"            
            android:layout_height="30dp"            
            android:layout_gravity="center_vertical"            
            android:layout_marginStart="15dp"            
            android:layout_marginTop="10dp"            
            android:layout_marginBottom="10dp"/>
        <TextView            
            android:id="@+id/submenu"            
            android:layout_width="wrap_content"            
            android:layout_height="wrap_content"            
            android:layout_gravity="center_vertical"            
            android:layout_marginStart="15dp"            
            android:textColor="@color/grayColor_55"            
            android:textSize="16sp"/>
        <!-- Indicator right-->        
        <LinearLayout            
            android:layout_width="match_parent"            
            android:layout_height="match_parent"     
            android:layout_marginStart="140dp"     
            android:layout_gravity="end">
            
            <ImageView        
                android:id="@+id/indicatorImage"   
                android:layout_width="30dp"        
                android:layout_height="30dp"        
                android:layout_gravity="center_vertical"      
                android:layout_marginStart="10dp"/>
        </LinearLayout>
    </LinearLayout>
</android.support.constraint.ConstraintLayout>


Add list_submenu.xml for Child list.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"    
    android:orientation="vertical"    
    android:layout_width="match_parent"    
    android:layout_height="match_parent">
    <!--Expandable View, Children List-->
    <TextView   
        android:id="@+id/submenu"   
        android:layout_width="wrap_content"    
        android:layout_height="wrap_content"    
        android:layout_marginStart="50dp"     
        android:padding="10dp"   
        android:textColor="@color/grayColor_55" 
        android:textSize="16sp"/>
</LinearLayout>

Now we've already had the xml files that we need.

Prepare Kotlin files

1. We need a model for set icon and text title.

ExpandedMenuModel.kt

class ExpandedMenuModel {

    var iconName = ""    var iconImg = -1 // menu icon resource id}


2.  We need a Adapter

ExpandableListAdapter.kt

class ExpandableListAdapter(private val mContext: Context, private val mListDataHeader: ArrayList<ExpandedMenuModel> // header titles                            , // child data in format of header title, child title                            private val mListDataChild: HashMap<ExpandedMenuModel, ArrayList<String>>, internal var expandList: ExpandableListView) : BaseExpandableListAdapter() {

    override fun getGroupCount(): Int {
        val i = mListDataHeader.size        
        Log.d("GROUPCOUNT", i.toString())
        return this.mListDataHeader.size    }

    override fun getChildrenCount(groupPosition: Int): Int {
        var childCount = 0        
        if (groupPosition == 2 || groupPosition == 3) {
            childCount = this.mListDataChild[this.mListDataHeader[groupPosition]]!!.size        
        }
        return childCount
    }

    override fun getGroup(groupPosition: Int): Any {
        return this.mListDataHeader[groupPosition]
    }

    override fun getChild(groupPosition: Int, childPosition: Int): Any {
        Log.d("CHILD", mListDataChild[this.mListDataHeader[groupPosition]]!!.get(childPosition))
        return mListDataChild[this.mListDataHeader[groupPosition]]!!.get(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 {
        var convertView = convertView
        val headerTitle = getGroup(groupPosition) as ExpandedMenuModel
        if (convertView == null) {
            val infalInflater = mContext                    
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
            convertView = infalInflater.inflate(R.layout.listheader, null)
        }
        val lblListHeader = convertView!!.findViewById(R.id.submenu) as TextView
        val headerIcon = convertView!!.findViewById(R.id.iconimage) as ImageView
        //  Expandable View, Indicator right        
        val headerIndicator = convertView.findViewById(R.id.indicatorImage) as ImageView
        lblListHeader.setTypeface(null, Typeface.NORMAL)
        lblListHeader.text = headerTitle.iconName        
        headerIcon.setImageResource(headerTitle.iconImg)
        // Expandable View, Indicator status        
        if (getChildrenCount( groupPosition ) == 0) {
            headerIndicator.visibility = View.GONE        
        } else {
            headerIndicator.visibility = View.VISIBLE            
            if (isExpanded) {
                headerIndicator.setBackgroundResource((R.drawable.ic_keyboard_arrow_up_black_18dp))
            } else {
                headerIndicator.setBackgroundResource((R.drawable.ic_keyboard_arrow_right_black_18dp))
            }
        }
        return convertView
    }

    override fun getChildView(groupPosition: Int, childPosition: Int, isLastChild: Boolean, convertView: View?, parent: ViewGroup): View {
        var convertView = convertView
        val childText = getChild(groupPosition, childPosition) as String

        if (convertView == null) {
            val infalInflater = this.mContext                    
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
            convertView = infalInflater.inflate(R.layout.list_submenu, null)
        }

        val txtListChild = convertView!!
                .findViewById(R.id.submenu) as TextView

        txtListChild.text = childText

        return convertView
    }

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


3. In MainActivity.kt , call your layout, add title and add onClickListener

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

    // ExpandableListView
    setNavigationView = findViewById<View>(R.id.naviView) as NavigationView

    setupDrawerContent(setNavigationView)

    // Add content in drawer
    prepareListData()

    mMenuAdapter = ExpandableListAdapter(this, listDataHeader, listDataChild, navigationmenu!!)
    navigationmenu!!.setAdapter(mMenuAdapter)
    //  Expandable View, hide original indicator    
    navigationmenu.setGroupIndicator(null)
    //*******************************************************************//

    // Set Listener, do anything you wanted
    // Group = Big Title, Child = SubTitle    
    navigationmenu.setOnGroupClickListener({ parent, _, groupPosition, _ ->        
    when (groupPosition) {
            // Position, first title = 0, second title = 1, .......
            0 -> {
                // Do anything you wanted
            }
            1 -> {
                // Do anything you wanted
} 2 -> { //Example - How to expand the drawer list if (parent.isGroupExpanded(groupPosition)) { parent.collapseGroup(groupPosition) } else { parent.expandGroup(groupPosition) parent.setOnChildClickListener({ parent, _, groupPosition, childPosition, _ -> when (childPosition) { 0 -> {
                                // Do anything you wanted
} 1 -> {
                                // Do anything you wanted
} }
                        // Collapse the expanded list
                        parent.collapseGroup(groupPosition)
                    })
                }
            }
            3 -> {
                if (parent.isGroupExpanded(groupPosition)) {
                    parent.collapseGroup(groupPosition)
                } else {
                    parent.expandGroup(groupPosition)
                    parent.setOnChildClickListener({ parent, _, groupPosition, childPosition, _ ->                        when (childPosition) {
                            0 -> {
                                // Do anything you wanted
} 1 -> {
                                // Do anything you wanted
} 2 -> {
                                // Do anything you wanted
} } parent.collapseGroup(groupPosition) }) } } 4 -> {
                // Do anything you wanted
} 5 -> {
                // Do anything you wanted
} } true }) }

private fun setupDrawerContent(navigationView: NavigationView?) {
    navigationView?.setNavigationItemSelectedListener { menuItem ->        
    selectDrawerItem(menuItem)
        true    
   }
}

//  ExpandableListView, Drawer content

// Add Anything you wanted

private fun prepareListData() {
    val drawerAddADDWII01 = ExpandedMenuModel()

    // First Part, group(Big Title)
    drawerAddADDWII01.iconName = getString(R.string.text_navi_add_device)
    drawerAddADDWII01.iconImg = R.drawable.ic_bluetooth_searching_black_24dp    
    // Adding data header    
    listDataHeader.add(drawerAddADDWII01)

    val drawerAccount02 = ExpandedMenuModel()
    drawerAccount02.iconName = getString(R.string.text_navi_accountManagement)
    drawerAccount02.iconImg = R.drawable.ic_account_box_black_24dp    listDataHeader.add(drawerAccount02)

    val drawerMap03 = ExpandedMenuModel()
    drawerMap03.iconName = getString(R.string.text_navi_title_map)
    drawerMap03.iconImg = R.drawable.ic_place_black_24dp    listDataHeader.add(drawerMap03)

    val drawerHelp04 = ExpandedMenuModel()
    drawerHelp04.iconName = getString(R.string.text_navi_title_help)
    drawerHelp04.iconImg = R.drawable.ic_help_black_24dp    listDataHeader.add(drawerHelp04)

    val drawerSetting05 = ExpandedMenuModel()
    drawerSetting05.iconName = getString(R.string.text_navi_setting)
    drawerSetting05.iconImg = R.drawable.ic_settings_black_24dp    listDataHeader.add(drawerSetting05)

    val drawerAbout06 = ExpandedMenuModel()
    drawerAbout06.iconName = getString(R.string.text_navi_about)
    drawerAbout06.iconImg = R.drawable.ic_phone_android_black_24dp    listDataHeader.add(drawerAbout06)

    // Second,  Adding child data (Subtitle)
    val childMap03 = ArrayList<String>()
    childMap03.add(getString(R.string.text_navi_personal))
    childMap03.add(getString(R.string.text_navi_air_map))

    val childHelp04 = ArrayList<String>()
    childHelp04.add(getString(R.string.text_navi_knowledge))
    childHelp04.add(getString(R.string.text_navi_q_and_a))
    childHelp04.add(getString(R.string.text_navi_tour))

    // This case, only 2 of Big title have sub title
    listDataChild[listDataHeader[2]] = childMap03
    listDataChild[listDataHeader[3]] = childHelp04
}


Thanks

1 則留言:

  1. You save my life ahahah.. I was looking for this implementation and I didn't find something updated in these days.. your solution gives me a better understanding of how to implement items Expandables

    Thanks alot!!

    回覆刪除