Default Navigation Drawer View to ExpandableListView
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>
Comments
-
envyM6 over 3 years
In Android Studio 2.1.2, if i create a default navigation activity I get this view:
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/>
usesapp:menu="@menu/activity_main_drawer"
for displaying list of menu defined inactivity_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 anExpandableListView
. Because I want my menu items to have subcategories, for example, menu itemCars
will have sub category ofDiesel
,Petrol
andHybrid
etc. I have done research and it appears no one has the exact working solution I need.I looked here:
- The Open Tutorials
- Implement expandablelistview in navigation drawer activity made by android studio
- Android: 2 or more ExpandableListView inside Navigation Drawer
- 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 usingmenu
item usingactivity_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)
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:
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:
Thanks @Moulesh !!!
-
envyM6 almost 8 yearsAwesome! Let me run the code and then I'll accept it!
-
Moulesh almost 8 yearsYou 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 almost 8 yearsI'm not actually! I'm pretty new to android and programming in general. Can you please post them here along with project structure?
-
envyM6 almost 8 yearsEXCELLENT. What other layout files i need for header text and child text?
-
envyM6 almost 8 yearsOMG 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 almost 8 yearsHow 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 almost 8 yearsI'm using the left indicators oly for now... I will let you know if i find any alternate soln for it..
-
remya thekkuvettil over 6 yearsThanks 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 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 over 6 yearsYou have used android:layout_marginTop="@dimen/nav_header_height" in your xml means you have the height of the headerView, not wrap_content
-
Moulesh over 6 yearsthe 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 over 6 yearsAs 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 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 over 6 yearsLet us continue this discussion in chat.
-
remya thekkuvettil over 6 years
-
Chitra Nandpal over 5 yearsits perfectly helped me! Thanks :)