Android ExpandableListView双层嵌套实现三级树形菜单
在android开发中,列表可以说是最常见的了,一般都是使用listview,当涉及到二维数组时,更多的使用到expandablelistview,然而当数据结构比较复杂时,就需要使用三级菜单或者更多级的菜单来显示,这就让人比较头疼了,最近做的项目就涉及到了三级菜单,遇到了不少问题,虽然不够完美,但是基本需求实现了,在此记录一下。(之前见过有人使用listview实现4级、5级甚至更多级菜单的,是在adapter的数据源里定义的结构,根据等级缩进左间距的倍数,链接地址找不到了,有兴趣的可以自己找找)
先上效果图:
简单介绍下重点,为了简便,把第一层expandablelistview称之为elistone,相应的adapter称之为adpone;第二层expandablelistview称之为elisttwo,相应的adapter称之为adptwo。
首先第一个要处理的问题是在adpone的getchildview方法中,需要对elisttwo的高度进行动态计算,因为elisttwo展开和关闭时高度是不一样的,所以要在elisttwo的setongroupexpandlistener和setongroupcollapselistener方法中做相应的处理:
/** * @author apathy、恒 * * 子expandablelistview展开时,因为group只有一项,所以子expandablelistview的总高度= * (子expandablelistview的child数量 + 1 )* 每一项的高度 * */ elistview.setongroupexpandlistener(new ongroupexpandlistener() { @override public void ongroupexpand(int groupposition) { layoutparams lp = new layoutparams(viewgroup.layoutparams.match_parent, (child.getchildnames().size() + 1)* (int) mcontext.getresources().getdimension(r.dimen.parent_expandable_list_height)); elistview.setlayoutparams(lp); } }); /** * @author apathy、恒 * * 子expandablelistview关闭时,此时只剩下group这一项,所以子expandablelistview的总高度即为一项的高度 * */ elistview.setongroupcollapselistener(new ongroupcollapselistener() { @override public void ongroupcollapse(int groupposition) { layoutparams lp = new layoutparams( viewgroup.layoutparams.match_parent, (int) mcontext .getresources().getdimension( r.dimen.parent_expandable_list_height)); elistview.setlayoutparams(lp); } });
只展示菜单肯定不是我们的最终需求,我们一般需要点击菜单后进行相应的界面跳转或者数据处理,所以就需要获取所点击的菜单精确下标,获取方法很简单,只需要定义一个接口,在adpone的getchildview方法中回调即可:
/** * @author apathy、恒 * * 点击子expandablelistview子项时,调用回调接口 * */ elistview.setonchildclicklistener(new onchildclicklistener() { @override public boolean onchildclick(expandablelistview arg0, view arg1, int groupindex, int childindex, long arg4) { if (mtreeviewclicklistener != null) { mtreeviewclicklistener.onclickposition(groupposition, childposition, childindex); } return false; } });
下面是完整的代码:
mainactivity.java:
package com.heng.tree; import java.util.arraylist; import com.heng.tree.r; import com.heng.tree.adapter.parentadapter; import com.heng.tree.adapter.parentadapter.onchildtreeviewclicklistener; import com.heng.tree.entity.childentity; import com.heng.tree.entity.parententity; import android.app.activity; import android.content.context; import android.graphics.color; import android.os.bundle; import android.widget.expandablelistview; import android.widget.expandablelistview.ongroupexpandlistener; import android.widget.toast; /** * * @author apathy、恒 * * <br/> * * @email shexiaoheng@163.com * * @blog * <a href='http://blog.csdn.net/shexiaoheng'>http://blog.csdn.net/shexiaoheng</a > * * <br/> * <br/> * * @detail 本demo为expandablelistview嵌套expandablelistview实现三级菜单的例子 * * #parentadapter.onchildtreeviewclicklistener * * */ public class mainactivity extends activity implements ongroupexpandlistener, onchildtreeviewclicklistener { private context mcontext; private expandablelistview elist; private arraylist<parententity> parents; private parentadapter adapter; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); mcontext = this; setcontentview(r.layout.activity_main); loaddata(); initelist(); } /** * @author apathy、恒 * * 初始化菜单数据源 * */ private void loaddata() { parents = new arraylist<parententity>(); for (int i = 0; i < 10; i++) { parententity parent = new parententity(); parent.setgroupname("父类父分组第" + i + "项"); parent.setgroupcolor(getresources().getcolor( android.r.color.holo_red_light)); arraylist<childentity> childs = new arraylist<childentity>(); for (int j = 0; j < 8; j++) { childentity child = new childentity(); child.setgroupname("子类父分组第" + j + "项"); child.setgroupcolor(color.parsecolor("#ff00ff")); arraylist<string> childnames = new arraylist<string>(); arraylist<integer> childcolors = new arraylist<integer>(); for (int k = 0; k < 5; k++) { childnames.add("子类第" + k + "项"); childcolors.add(color.parsecolor("#ff00ff")); } child.setchildnames(childnames); childs.add(child); } parent.setchilds(childs); parents.add(parent); } } /** * @author apathy、恒 * * 初始化expandablelistview * */ private void initelist() { elist = (expandablelistview) findviewbyid(r.id.elist); elist.setongroupexpandlistener(this); adapter = new parentadapter(mcontext, parents); elist.setadapter(adapter); adapter.setonchildtreeviewclicklistener(this); } /** * @author apathy、恒 * * 点击子expandablelistview的子项时,回调本方法,根据下标获取值来做相应的操作 * */ @override public void onclickposition(int parentposition, int groupposition, int childposition) { // do something string childname = parents.get(parentposition).getchilds() .get(groupposition).getchildnames().get(childposition) .tostring(); toast.maketext( mcontext, "点击的下标为: parentposition=" + parentposition + " groupposition=" + groupposition + " childposition=" + childposition + "\n点击的是:" + childname, toast.length_short).show(); } /** * @author apathy、恒 * * 展开一项,关闭其他项,保证每次只能展开一项 * */ @override public void ongroupexpand(int groupposition) { for (int i = 0; i < parents.size(); i++) { if (i != groupposition) { elist.collapsegroup(i); } } } }
parentadapter.java
package com.heng.tree.adapter; import java.util.arraylist; import android.content.context; import android.view.layoutinflater; import android.view.view; import android.view.viewgroup; import android.widget.baseexpandablelistadapter; import android.widget.expandablelistview; import android.widget.expandablelistview.onchildclicklistener; import android.widget.expandablelistview.ongroupcollapselistener; import android.widget.expandablelistview.ongroupexpandlistener; import android.widget.abslistview.layoutparams; import android.widget.textview; import com.heng.tree.r; import com.heng.tree.entity.childentity; import com.heng.tree.entity.parententity; /** * * @author apathy、恒 * * 父类分组的适配器 * * <br/> * <br/> * * 方法 {@link #getchildview(int, int, boolean, view, viewgroup)}<b><font * color='#ff00ff' size='2'>极其重要</font></b> * * */ public class parentadapter extends baseexpandablelistadapter { private context mcontext;// 上下文 private arraylist<parententity> mparents;// 数据源 private onchildtreeviewclicklistener mtreeviewclicklistener;// 点击子expandablelistview子项的监听 public parentadapter(context context, arraylist<parententity> parents) { this.mcontext = context; this.mparents = parents; } @override public childentity getchild(int groupposition, int childposition) { return mparents.get(groupposition).getchilds().get(childposition); } @override public long getchildid(int groupposition, int childposition) { return childposition; } @override public int getchildrencount(int groupposition) { return mparents.get(groupposition).getchilds() != null ? mparents .get(groupposition).getchilds().size() : 0; } @override public view getchildview(final int groupposition, final int childposition, boolean isexpanded, view convertview, viewgroup parent) { final expandablelistview elistview = getexpandablelistview(); arraylist<childentity> childs = new arraylist<childentity>(); final childentity child = getchild(groupposition, childposition); childs.add(child); final childadapter childadapter = new childadapter(this.mcontext, childs); elistview.setadapter(childadapter); /** * @author apathy、恒 * * 点击子expandablelistview子项时,调用回调接口 * */ elistview.setonchildclicklistener(new onchildclicklistener() { @override public boolean onchildclick(expandablelistview arg0, view arg1, int groupindex, int childindex, long arg4) { if (mtreeviewclicklistener != null) { mtreeviewclicklistener.onclickposition(groupposition, childposition, childindex); } return false; } }); /** * @author apathy、恒 * * 子expandablelistview展开时,因为group只有一项,所以子expandablelistview的总高度= * (子expandablelistview的child数量 + 1 )* 每一项的高度 * */ elistview.setongroupexpandlistener(new ongroupexpandlistener() { @override public void ongroupexpand(int groupposition) { layoutparams lp = new layoutparams( viewgroup.layoutparams.match_parent, (child .getchildnames().size() + 1) * (int) mcontext.getresources().getdimension( r.dimen.parent_expandable_list_height)); elistview.setlayoutparams(lp); } }); /** * @author apathy、恒 * * 子expandablelistview关闭时,此时只剩下group这一项, * 所以子expandablelistview的总高度即为一项的高度 * */ elistview.setongroupcollapselistener(new ongroupcollapselistener() { @override public void ongroupcollapse(int groupposition) { layoutparams lp = new layoutparams( viewgroup.layoutparams.match_parent, (int) mcontext .getresources().getdimension( r.dimen.parent_expandable_list_height)); elistview.setlayoutparams(lp); } }); return elistview; } /** * @author apathy、恒 * * 动态创建子expandablelistview * */ public expandablelistview getexpandablelistview() { expandablelistview mexpandablelistview = new expandablelistview( mcontext); layoutparams lp = new layoutparams( viewgroup.layoutparams.match_parent, (int) mcontext .getresources().getdimension( r.dimen.parent_expandable_list_height)); mexpandablelistview.setlayoutparams(lp); mexpandablelistview.setdividerheight(0);// 取消group项的分割线 mexpandablelistview.setchilddivider(null);// 取消child项的分割线 mexpandablelistview.setgroupindicator(null);// 取消展开折叠的指示图标 return mexpandablelistview; } @override public object getgroup(int groupposition) { return mparents.get(groupposition); } @override public int getgroupcount() { return mparents != null ? mparents.size() : 0; } @override public long getgroupid(int groupposition) { return groupposition; } @override public view getgroupview(int groupposition, boolean isexpanded, view convertview, viewgroup parent) { groupholder holder = null; if (convertview == null) { convertview = layoutinflater.from(mcontext).inflate( r.layout.parent_group_item, null); holder = new groupholder(convertview); convertview.settag(holder); } else { holder = (groupholder) convertview.gettag(); } holder.update(mparents.get(groupposition)); return convertview; } /** * @author apathy、恒 * * holder优化 * */ class groupholder { private textview parentgrouptv; public groupholder(view v) { parentgrouptv = (textview) v.findviewbyid(r.id.parentgrouptv); } public void update(parententity model) { parentgrouptv.settext(model.getgroupname()); parentgrouptv.settextcolor(model.getgroupcolor()); } } @override public boolean hasstableids() { return false; } @override public boolean ischildselectable(int groupposition, int childposition) { return false; } /** * @author apathy、恒 * * 设置点击子expandablelistview子项的监听 * */ public void setonchildtreeviewclicklistener( onchildtreeviewclicklistener treeviewclicklistener) { this.mtreeviewclicklistener = treeviewclicklistener; } /** * @author apathy、恒 * * 点击子expandablelistview子项的回调接口 * */ public interface onchildtreeviewclicklistener { void onclickposition(int parentposition, int groupposition, int childposition); } }
childadapter.java
package com.heng.tree.adapter; import java.util.arraylist; import com.heng.tree.r; import com.heng.tree.entity.childentity; import android.content.context; import android.graphics.color; import android.view.layoutinflater; import android.view.view; import android.view.viewgroup; import android.widget.baseexpandablelistadapter; import android.widget.textview; /** * * @author apathy、恒 * * <br/> * <br/> * * 子类分组的适配器 * * <br/> * <br/> * * 方法{@link #ischildselectable(int,int)} <b><font color='#ff00ff' * size='2'>必须返回true</font></b> * * */ public class childadapter extends baseexpandablelistadapter { private context mcontext;// 上下文 private arraylist<childentity> mchilds;// 数据源 public childadapter(context context, arraylist<childentity> childs) { this.mcontext = context; this.mchilds = childs; } @override public int getchildrencount(int groupposition) { return mchilds.get(groupposition).getchildnames() != null ? mchilds .get(groupposition).getchildnames().size() : 0; } @override public string getchild(int groupposition, int childposition) { if (mchilds.get(groupposition).getchildnames() != null && mchilds.get(groupposition).getchildnames().size() > 0) return mchilds.get(groupposition).getchildnames() .get(childposition).tostring(); return null; } @override public long getchildid(int groupposition, int childposition) { return childposition; } @override public view getchildview(int groupposition, int childposition, boolean isexpanded, view convertview, viewgroup parent) { childholder holder = null; if (convertview == null) { convertview = layoutinflater.from(mcontext).inflate( r.layout.child_child_item, null); holder = new childholder(convertview); convertview.settag(holder); } else { holder = (childholder) convertview.gettag(); } holder.update(getchild(groupposition, childposition)); return convertview; } /** * @author apathy、恒 * * holder优化 * */ class childholder { private textview childchildtv; public childholder(view v) { childchildtv = (textview) v.findviewbyid(r.id.childchildtv); } public void update(string str) { childchildtv.settext(str); childchildtv.settextcolor(color.parsecolor("#00ffff")); } } @override public object getgroup(int groupposition) { if (mchilds != null && mchilds.size() > 0) return mchilds.get(groupposition); return null; } @override public int getgroupcount() { return mchilds != null ? mchilds.size() : 0; } @override public long getgroupid(int groupposition) { return groupposition; } @override public view getgroupview(int groupposition, boolean isexpanded, view convertview, viewgroup parent) { groupholder holder = null; if (convertview == null) { convertview = layoutinflater.from(mcontext).inflate( r.layout.child_group_item, null); holder = new groupholder(convertview); convertview.settag(holder); } else { holder = (groupholder) convertview.gettag(); } holder.update(mchilds.get(groupposition)); return convertview; } /** * @author apathy、恒 * * holder优化 * */ class groupholder { private textview childgrouptv; public groupholder(view v) { childgrouptv = (textview) v.findviewbyid(r.id.childgrouptv); } public void update(childentity model) { childgrouptv.settext(model.getgroupname()); childgrouptv.settextcolor(model.getgroupcolor()); } } @override public boolean hasstableids() { return false; } @override public boolean ischildselectable(int groupposition, int childposition) { /** * ============================================== * 此处必须返回true,否则无法响应子项的点击事件=============== * ============================================== **/ return true; } }
clistadapter.java
package com.heng.tree.adapter; import java.util.arraylist; import com.heng.tree.r; import com.heng.tree.entity.childentity; import android.content.context; import android.view.layoutinflater; import android.view.view; import android.view.viewgroup; import android.widget.baseadapter; import android.widget.textview; /** * * @author apathy、恒 * * 子类子类列表的适配器 * * */ public class clistadapter extends baseadapter { private context mcontext; private arraylist<childentity> mchilds; public clistadapter(context context, arraylist<childentity> childs) { this.mcontext = context; this.mchilds = childs; } @override public int getcount() { return mchilds != null ? mchilds.size() : 0; } @override public object getitem(int position) { if ((getcount() > 0) && (position > 0 && position < mchilds.size())) { return mchilds.get(position); } return null; } @override public long getitemid(int position) { return position; } @override public view getview(int position, view convertview, viewgroup parent) { holder holder = null; if (convertview == null) { convertview = layoutinflater.from(mcontext).inflate( r.layout.child_child_item, null); holder = new holder(convertview); convertview.settag(holder); } else { holder = (holder) convertview.gettag(); } holder.update(mchilds.get(position).getgroupname()); return convertview; } class holder { private textview tv; public holder(view v) { tv = (textview) v.findviewbyid(r.id.childchildtv); } public void update(string text) { tv.settext(text); } } }
parententity.java
package com.heng.tree.entity; import java.util.arraylist; /** * * @author apathy、恒 * * 子类分组的实体 * * */ public class parententity { private int groupcolor; private string groupname; private arraylist<childentity> childs; /* ========================================================== * ======================= get method ======================= * ========================================================== */ public int getgroupcolor() { return groupcolor; } public string getgroupname() { return groupname; } public arraylist<childentity> getchilds() { return childs; } /* ========================================================== * ======================= set method ======================= * ========================================================== */ public void setgroupcolor(int groupcolor) { this.groupcolor = groupcolor; } public void setgroupname(string groupname) { this.groupname = groupname; } public void setchilds(arraylist<childentity> childs) { this.childs = childs; } }
childentity.java
package com.heng.tree.entity; import java.util.arraylist; /** * * @author apathy、恒 * * 父类分组的实体 * * */ public class childentity { private int groupcolor; private string groupname; private arraylist<string> childnames; /* ========================================================== * ======================= get method ======================= * ========================================================== */ public int getgroupcolor() { return groupcolor; } public string getgroupname() { return groupname; } public arraylist<string> getchildnames() { return childnames; } /* ========================================================== * ======================= set method ======================= * ========================================================== */ public void setgroupcolor(int groupcolor) { this.groupcolor = groupcolor; } public void setgroupname(string groupname) { this.groupname = groupname; } public void setchildnames(arraylist<string> childnames) { this.childnames = childnames; } }
activity_main.xml
<framelayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <expandablelistview android:id="@+id/elist" android:layout_width="match_parent" android:layout_height="match_parent" android:groupindicator="@null" /> </framelayout>
parent_group_item.xml
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingleft="@dimen/parent_expandable_list_group_padding_left" > <textview android:id="@+id/parentgrouptv" android:layout_width="wrap_content" android:layout_height="@dimen/parent_expandable_list_height" android:gravity="center_vertical" /> </relativelayout>
child_group_item.xml
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" > <relativelayout android:layout_width="match_parent" android:layout_height="@dimen/parent_expandable_list_height" > <textview android:id="@+id/childgrouptv" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centervertical="true" android:paddingleft="@dimen/child_expandable_list_group_padding_left" /> </relativelayout> </linearlayout>
child_child_item.xml
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" > <relativelayout android:layout_width="match_parent" android:layout_height="@dimen/parent_expandable_list_height" > <textview android:id="@+id/childchildtv" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centervertical="true" android:paddingleft="@dimen/child_expandable_list_child_padding_left" /> </relativelayout> </linearlayout>
dimens.xml
<resources> <!-- default screen margins, per the android design guidelines. --> <dimen name="parent_expandable_list_height">50dp</dimen> <dimen name="parent_expandable_list_group_padding_left">10dp</dimen> <dimen name="child_expandable_list_group_padding_left">40dp</dimen> <dimen name="child_expandable_list_child_padding_left">75dp</dimen> </resources>
点此下载demo
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。