欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android ExpandableListView双层嵌套实现三级树形菜单

程序员文章站 2023-11-14 14:38:46
在android开发中,列表可以说是最常见的了,一般都是使用listview,当涉及到二维数组时,更多的使用到expandablelistview,然而当数据结构比较复杂时...

在android开发中,列表可以说是最常见的了,一般都是使用listview,当涉及到二维数组时,更多的使用到expandablelistview,然而当数据结构比较复杂时,就需要使用三级菜单或者更多级的菜单来显示,这就让人比较头疼了,最近做的项目就涉及到了三级菜单,遇到了不少问题,虽然不够完美,但是基本需求实现了,在此记录一下。(之前见过有人使用listview实现4级、5级甚至更多级菜单的,是在adapter的数据源里定义的结构,根据等级缩进左间距的倍数,链接地址找不到了,有兴趣的可以自己找找)

先上效果图:

Android ExpandableListView双层嵌套实现三级树形菜单

Android ExpandableListView双层嵌套实现三级树形菜单

简单介绍下重点,为了简便,把第一层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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。