Default Navigation Drawer View to ExpandableListView
Asked Answered
R

2

10

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 !!!

Rockribbed answered 4/8, 2016 at 11:13 Comment(6)
Have you tried to implement expandable list on drawerMurray
Hi @Murray yes I'm trying to but no success and no progress. Ive created a new layout file with expandable list view and trying to include that into the main_activity.xml. Im just not getting it right. Any ideas?Rockribbed
i know the pain.. there is no work around anywhere on the web... i learnt how to create expandable list first... then did lot of research to merge it on drawer... Hope my solution helps u and get your work done faster...Murray
I'm pretty sure you can make sub menu items from the menu Xml resource. I don't know about it being expandable, thoughDustidustie
yes I need collapsible menuRockribbed
In your activity_main.xml you need to replace NavigationView by ` ExpandableListView` (and of course add all the classes/resources required by the latter).Sempiternal
M
26

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>
Murray answered 4/8, 2016 at 12:14 Comment(15)
Awesome! Let me run the code and then I'll accept it!Rockribbed
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 hereMurray
I'm not actually! I'm pretty new to android and programming in general. Can you please post them here along with project structure?Rockribbed
EXCELLENT. What other layout files i need for header text and child text?Rockribbed
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 @MurrayRockribbed
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); }Rockribbed
I'm using the left indicators oly for now... I will let you know if i find any alternate soln for it..Murray
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)Suzy
@remyathekkuvettil even in my answer the height of header view wrap_content only right... Actually I'm not understanding your issue..Murray
You have used android:layout_marginTop="@dimen/nav_header_height" in your xml means you have the height of the headerView, not wrap_contentSuzy
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 headerMurray
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.Suzy
@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.Murray
Let us continue this discussion in chat.Suzy
its perfectly helped me! Thanks :)Melatonin
X
0

Download source code from here(Navigation drawer with expandablelistview in android)

 import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.TextView;

import java.util.ArrayList;


public class CountryAdapter extends BaseExpandableListAdapter {
    Context context;
    ArrayList<Model_country> al_country;

    public CountryAdapter(Context context, ArrayList<Model_country> al_country) {
        this.context = context;
        this.al_country = al_country;
    }

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

    @Override
    public int getChildrenCount(int i) {
        return al_country.get(i).getAl_state().size();
    }

    @Override
    public Object getGroup(int i) {
        return al_country.get(i);
    }

    @Override
    public Object getChild(int i, int i1) {
        return al_country.get(i).getAl_state().get(i1);
    }

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

    @Override
    public long getChildId(int i, int i1) {
        return i1;
    }

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

    @Override
    public View getGroupView(int i, boolean b, View view, ViewGroup viewGroup) {
        if (view==null){

            LayoutInflater layoutInflater = (LayoutInflater) this.context
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            view = layoutInflater.inflate(R.layout.adapter_header, null);
        }
        TextView tv_state = (TextView)view.findViewById(R.id.tv_name);
        tv_state.setText(al_country.get(i).getStr_country());
        return view;
    }

    @Override
    public View getChildView(final int i, final int i1, boolean b, View view, ViewGroup viewGroup) {
        if (view==null){

            LayoutInflater layoutInflater = (LayoutInflater) this.context
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            view = layoutInflater.inflate(R.layout.adapter_childview, null);
        }

        TextView tv_state = (TextView)view.findViewById(R.id.tv_name);

        tv_state.setText(al_country.get(i).getAl_state().get(i1).getStr_name());
        tv_state.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                ((MainActivity)context).fn_selectedPosition(i,i1);
            }
        });
        return view;
    }

    @Override
    public boolean isChildSelectable(int i, int i1) {
        return false;
    }


}

Thanks!

Xantho answered 25/9, 2017 at 5:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.