Default Navigation Drawer View to ExpandableListView

20,029

This is the code for expandable list adapter:

public class ExpandListAdapter extends BaseExpandableListAdapter {
private Context _context;
private List<String> _listDataHeader; // header titles
// child data in format of header title, child title
private HashMap<String, List<String>> _listDataChild;

public ExpandListAdapter(Context context, List<String> listDataHeader,
                         HashMap<String, List<String>> listChildData) {
    this._context = context;
    this._listDataHeader = listDataHeader;
    this._listDataChild = listChildData;
}

@Override
public Object getChild(int groupPosition, int childPosititon) {
    return this._listDataChild.get(this._listDataHeader.get(groupPosition))
            .get(childPosititon);
}

@Override
public long getChildId(int groupPosition, int childPosition) {
    return childPosition;
}

@Override
public View getChildView(int groupPosition, final int childPosition,
                         boolean isLastChild, View convertView, ViewGroup parent) {

    final String childText = (String) getChild(groupPosition, childPosition);

    if (convertView == null) {
        LayoutInflater infalInflater = (LayoutInflater) this._context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = infalInflater.inflate(R.layout.list_item, null);
    }

    TextView txtListChild = (TextView) convertView
            .findViewById(R.id.lblListItem);

    txtListChild.setText(childText);
    return convertView;
}

@Override
public int getChildrenCount(int groupPosition) {
    return this._listDataChild.get(this._listDataHeader.get(groupPosition))
            .size();
}

@Override
public Object getGroup(int groupPosition) {
    return this._listDataHeader.get(groupPosition);
}

@Override
public int getGroupCount() {
    return this._listDataHeader.size();
}

@Override
public long getGroupId(int groupPosition) {
    return groupPosition;
}

@Override
public View getGroupView(int groupPosition, boolean isExpanded,
                         View convertView, ViewGroup parent) {
    String headerTitle = (String) getGroup(groupPosition);
    if (convertView == null) {
        LayoutInflater infalInflater = (LayoutInflater) this._context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = infalInflater.inflate(R.layout.list_group, null);
    }

    TextView lblListHeader = (TextView) convertView
            .findViewById(R.id.lblListHeader);
    lblListHeader.setTypeface(null, Typeface.BOLD);
    lblListHeader.setText(headerTitle);

    return convertView;
}

@Override
public boolean hasStableIds() {
    return false;
}

@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
    return true;
}}

In your activity:

     private void enableExpandableList() {
      listDataHeader = new ArrayList<String>();
      listDataChild = new HashMap<String, List<String>>();
    expListView = (ExpandableListView) findViewById(R.id.left_drawer);

    prepareListData(listDataHeader, listDataChild);
    listAdapter = new ExpandListAdapter(this, listDataHeader, listDataChild);
    // setting list adapter
    expListView.setAdapter(listAdapter);

    expListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {

        @Override
        public boolean onGroupClick(ExpandableListView parent, View v,
                                    int groupPosition, long id) {
            // Toast.makeText(getApplicationContext(),
            // "Group Clicked " + listDataHeader.get(groupPosition),
            // Toast.LENGTH_SHORT).show();
            return false;
        }
    });
    // Listview Group expanded listener
    expListView.setOnGroupExpandListener(new ExpandableListView.OnGroupExpandListener() {

        @Override
        public void onGroupExpand(int groupPosition) {
            Toast.makeText(getApplicationContext(),
                    listDataHeader.get(groupPosition) + " Expanded",
                    Toast.LENGTH_SHORT).show();
        }
    });

    // Listview Group collasped listener
    expListView.setOnGroupCollapseListener(new ExpandableListView.OnGroupCollapseListener() {

        @Override
        public void onGroupCollapse(int groupPosition) {
            Toast.makeText(getApplicationContext(),
                    listDataHeader.get(groupPosition) + " Collapsed",
                    Toast.LENGTH_SHORT).show();

        }
    });

    // Listview on child click listener
    expListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {

        @Override
        public boolean onChildClick(ExpandableListView parent, View v,
                                    int groupPosition, int childPosition, long id) {
            // TODO Auto-generated method stub
            // Temporary code:

            // till here
            Toast.makeText(
                    getApplicationContext(),
                    listDataHeader.get(groupPosition)
                            + " : "
                            + listDataChild.get(
                            listDataHeader.get(groupPosition)).get(
                            childPosition), Toast.LENGTH_SHORT)
                    .show();
            return false;
        }
    });}

Method to create List with data:

   private void prepareListData(List<String> listDataHeader, Map<String,
  List<String>> listDataChild) {


    // Adding child data
    listDataHeader.add("Product1");
    listDataHeader.add("product2");
    listDataHeader.add("Product3");

    // Adding child data
    List<String> top = new ArrayList<String>();
    top.add("x1");
    top.add("x2");
    top.add("x3");
    top.add("x4");
    top.add("x5");


    List<String> mid = new ArrayList<String>();
    mid.add("y1");
    mid.add("y2");
    mid.add("y3");

    List<String> bottom = new ArrayList<String>();
    bottom.add("z1");
    bottom.add("z2");
    bottom.add("z3");



    listDataChild.put(listDataHeader.get(0), top); // Header, Child data
    listDataChild.put(listDataHeader.get(1), mid);
    listDataChild.put(listDataHeader.get(2), bottom);
}

this code in layout xml

<?xml version="1.0" encoding="utf-8"?>
    <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/drawer_layout"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:fitsSystemWindows="true"
     tools:openDrawer="start">

<include
    android:id="@+id/act_bar"
    layout="@layout/app_bar_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

<android.support.design.widget.NavigationView
    android:id="@+id/nav_view"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_gravity="start"
    android:fitsSystemWindows="true"
    app:headerLayout="@layout/nav_header_main">
    <!--app:menu="@menu/activity_main_drawer"-->

    <ExpandableListView
        android:id="@+id/left_drawer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/nav_header_height"
        android:background="@color/Background"
        android:dividerHeight="0dp" />

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

xml for ExplistHeader list_group.xml

    <?xml version="1.0" encoding="utf-8"?>
        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:paddingTop="10dp"
        android:padding="30dp"
        android:background="@color/Background">

        <TextView
            android:id="@+id/lblListHeader"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:paddingLeft="?
            android:attr/expandableListPreferredItemPaddingLeft"
            android:textSize="17dp"
            android:textColor="@color/colorTextPrimary" />    
        </LinearLayout>

xml for Explistchild list_item.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="55dip"
        android:background="@color/Background"
        android:orientation="vertical" >

        <TextView
            android:id="@+id/lblListItem"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:textSize="17dip"
            android:paddingTop="5dp"
            android:paddingBottom="5dp"
            android:paddingLeft="?
            android:attr/expandableListPreferredChildPaddingLeft" />
        </LinearLayout>
Share:
20,029
envyM6
Author by

envyM6

The real novice to the coding world

Updated on October 31, 2020

Comments

  • envyM6
    envyM6 over 3 years

    In Android Studio 2.1.2, if i create a default navigation activity I get this view:

    enter image description here

    Which uses the following activity_main.xml file:

    <?xml version="1.0" encoding="utf-8"?>
    <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/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        tools:openDrawer="start">
    
        <include
            layout="@layout/app_bar_main"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    
        <android.support.design.widget.NavigationView
            android:id="@+id/nav_view"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            android:fitsSystemWindows="true"
            app:headerLayout="@layout/nav_header_main"
            app:menu="@menu/activity_main_drawer" />
    
    </android.support.v4.widget.DrawerLayout>
    

    As you can see <android.support.design.widget.NavigationView/> uses app:menu="@menu/activity_main_drawer" for displaying list of menu defined in activity_main_drawer.xml file as follows:

    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android">
    
        <group android:checkableBehavior="single">
            <item
                android:id="@+id/nav_camera"
                android:icon="@drawable/ic_menu_camera"
                android:title="Import" />
            <item
                android:id="@+id/nav_gallery"
                android:icon="@drawable/ic_menu_gallery"
                android:title="Gallery" />
            <item
                android:id="@+id/nav_slideshow"
                android:icon="@drawable/ic_menu_slideshow"
                android:title="Slideshow" />
            <item
                android:id="@+id/nav_manage"
                android:icon="@drawable/ic_menu_manage"
                android:title="Tools" />
        </group>
    
        <item android:title="Communicate">
            <menu>
                <item
                    android:id="@+id/nav_share"
                    android:icon="@drawable/ic_menu_share"
                    android:title="Share" />
                <item
                    android:id="@+id/nav_send"
                    android:icon="@drawable/ic_menu_send"
                    android:title="Send" />
            </menu>
        </item>
    
    </menu>
    

    Now my plan is to replace this menu list defined in activity_main_drawer.xml and instead use an ExpandableListView. Because I want my menu items to have subcategories, for example, menu item Cars will have sub category of Diesel, Petrol and Hybrid etc. I have done research and it appears no one has the exact working solution I need.

    I looked here:

    1. The Open Tutorials
    2. Implement expandablelistview in navigation drawer activity made by android studio
    3. Android: 2 or more ExpandableListView inside Navigation Drawer
    4. How to create an expandable listView inside navigation drawer?

    and the subsequent links mentioned there.

    N.B: The links above mentions the use of ListView in navigation drawer which is no longer the case as Android Studio achieves this by using menu item using activity_main_drawer.xml.

    Can someone please provide me an working example of this? To re-iterate, I want an expandable list view inside the default navigation drawer activity. Ive gathered that I'll need XML files and java class codes to get me the foundation I need to get started.

    Thanks in advance. :)

    EDIT: Final look mockup (excuse my photoshop skills)enter image description here

    My current approach is to create a new layout ex_list as follows:

    <?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">
        <android.support.v4.widget.DrawerLayout
            android:id="@+id/drawer_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
           <ExpandableListView
                android:id="@+id/lvExp"
                android:layout_height="match_parent"
                android:layout_width="match_parent"/>
    
        </android.support.v4.widget.DrawerLayout>
    </LinearLayout>
    

    and edit activity_main.xml to

    <?xml version="1.0" encoding="utf-8"?>
    <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/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true"
        tools:openDrawer="start">
    
        <include
            layout="@layout/app_bar_main"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    
        <android.support.design.widget.NavigationView
            android:id="@+id/nav_view"
            android:layout_width="240dp"
            android:layout_gravity = "start"
            android:layout_height="match_parent"
    
            android:fitsSystemWindows="true"
            app:headerLayout="@layout/nav_header_main">
            <include
                layout="@layout/ex_list"
                android:layout_height="wrap_content"
                android:layout_width="match_parent"/>
    
        </android.support.design.widget.NavigationView>
    
    
    </android.support.v4.widget.DrawerLayout>
    

    Which results to following layout:

    enter image description here

    and error message of

                                                                          java.lang.IllegalArgumentException: DrawerLayout must be measured with MeasureSpec.EXACTLY.
                                                                              at android.support.v4.widget.DrawerLayout.onMeasure(DrawerLayout.java:1036)
                                                                              at android.view.View.measure(View.java:18788)
                                                                              at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
                                                                              at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465)
                                                                              at android.widget.LinearLayout.measureVertical(LinearLayout.java:748)
                                                                              at android.widget.LinearLayout.onMeasure(LinearLayout.java:630)
                                                                              at android.view.View.measure(View.java:18788)
                                                                              at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
                                                                              at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
                                                                              at android.support.design.widget.NavigationView.onMeasure(NavigationView.java:223)
                                                                              at android.view.View.measure(View.java:18788)
                                                                              at android.support.v4.widget.DrawerLayout.onMeasure(DrawerLayout.java:1104)
                                                                              at android.view.View.measure(View.java:18788)
                                                                              at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
                                                                              at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
                                                                              at android.support.v7.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:135)
                                                                              at android.view.View.measure(View.java:18788)
                                                                              at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
                                                                              at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465)
                                                                              at android.widget.LinearLayout.measureVertical(LinearLayout.java:748)
                                                                              at android.widget.LinearLayout.onMeasure(LinearLayout.java:630)
                                                                              at android.view.View.measure(View.java:18788)
                                                                              at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
                                                                              at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
                                                                              at android.view.View.measure(View.java:18788)
                                                                              at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
                                                                              at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465)
                                                                              at android.widget.LinearLayout.measureVertical(LinearLayout.java:748)
                                                                              at android.widget.LinearLayout.onMeasure(LinearLayout.java:630)
                                                                              at android.view.View.measure(View.java:18788)
                                                                              at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5951)
                                                                              at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
                                                                              at com.android.internal.policy.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2643)
                                                                              at android.view.View.measure(View.java:18788)
                                                                              at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2100)
                                                                              at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1216)
                                                                              at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1452)
                                                                              at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1107)
                                                                              at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6013)
                                                                              at android.view.Choreographer$CallbackRecord.run(Choreographer.java:858)
                                                                              at android.view.Choreographer.doCallbacks(Choreographer.java:670)
                                                                              at android.view.Choreographer.doFrame(Choreographer.java:606)
                                                                              at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:844)
                                                                              at android.os.Handler.handleCallback(Handler.java:739)
                                                                              at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                              at android.os.Looper.loop(Looper.java:148)
                                                                              at android.app.ActivityThread.main(ActivityThread.java:5417)
                                                                              at java.lang.reflect.Method.invoke(Native Method)
                                                                              at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                                                                              at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
    

    What am I doing wrong?

    ANSWER:

    enter image description here

    Thanks @Moulesh !!!

  • envyM6
    envyM6 almost 8 years
    Awesome! Let me run the code and then I'll accept it!
  • Moulesh
    Moulesh almost 8 years
    You are aware of xml files that needs to be created for ExpandableList Header Text and child texts right?? coz i haven't posted them here
  • envyM6
    envyM6 almost 8 years
    I'm not actually! I'm pretty new to android and programming in general. Can you please post them here along with project structure?
  • envyM6
    envyM6 almost 8 years
    EXCELLENT. What other layout files i need for header text and child text?
  • envyM6
    envyM6 almost 8 years
    OMG man you saved countless hours of my time! Now I can get started to make my app! I simply cant thank you enough! Thanks @Moulesh
  • envyM6
    envyM6 almost 8 years
    How can I set the group indicators to the right? I was doing it by private void setGroupIndicatorToRight() { DisplayMetrics dm=new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm); int width=dm.widthPixels; expListView.setIndicatorBounds(width - getDipsFromPixel(35), width - getDipsFromPixel(5)); } public int getDipsFromPixel(float pixels) { final float scale = getResources().getDisplayMetrics().density; return (int) (pixels * scale + 0.5f); }
  • Moulesh
    Moulesh almost 8 years
    I'm using the left indicators oly for now... I will let you know if i find any alternate soln for it..
  • remya thekkuvettil
    remya thekkuvettil over 6 years
    Thanks for the answer. But what if the height of the header view is not directly available(suppose the header view layout has height wrap_content)
  • Moulesh
    Moulesh over 6 years
    @remyathekkuvettil even in my answer the height of header view wrap_content only right... Actually I'm not understanding your issue..
  • remya thekkuvettil
    remya thekkuvettil over 6 years
    You have used android:layout_marginTop="@dimen/nav_header_height" in your xml means you have the height of the headerView, not wrap_content
  • Moulesh
    Moulesh over 6 years
    the header height should be a static value... if its a wrap_content it doesn't work... You can also check the google design specifications to see what is the default height of navigation header
  • remya thekkuvettil
    remya thekkuvettil over 6 years
    As my requirement the header view can take any layout. So if the layout has a height of wrap_content, it should work. So i used onWindowFocusChanged to get the exact height of the header view.
  • Moulesh
    Moulesh over 6 years
    @remyathekkuvettil Im not sure if its possible but one thing u can try is to merge everything to header layout and show them as different layouts in your navigation drawer.
  • remya thekkuvettil
    remya thekkuvettil over 6 years
  • remya thekkuvettil
    remya thekkuvettil over 6 years
  • Chitra Nandpal
    Chitra Nandpal over 5 years
    its perfectly helped me! Thanks :)