Android提高之多级树形菜单的实现方法

时间:2021-08-25 08:09:40

一般来说在android里要实现树形菜单,都是用expandablelist(也有高手自己继承listview或者linearlayout来做),但是expandablelist一般只能实现2级树形菜单。本文所述实例也依然使用expandablelist,但是要实现的是3级树形菜单。

本文程序运行效果图如下图所示:

Android提高之多级树形菜单的实现方法

当用baseexpandablelistadapter来实现二级树形菜单时,父项(getgroupview())和子项(getchildview())都是使用textview。当要实现三级树形菜单时,子项(getchildview())就必须使用expandablelist了。另外还要定义结构体来方便调用三级树形的数据,二级树形菜单可以用如下:

?
1
2
3
4
static public class treenode{
 object parent;
 list<object> childs=new arraylist<object>();
}

三级树形菜单可以用如下,子项是二级树形菜单的结构体如下所示:

?
1
2
3
4
5
static public class supertreenode {
 object parent;
 //二级树形菜单的结构体
 list<treeviewadapter.treenode> childs = new arraylist<treeviewadapter.treenode>();
}

实现三级树形菜单有两点要注意的:

1、第二级也是个树形菜单,因此必须在第二级项目展开/回收时设置足够的空间来完全显示二级树形菜单;

2、在实现三级树形菜单时,发现菜单的方法都是用不了(如onchildclicklistener、ongroupclicklistener等),因此要获得选中的数据就必须在外部定义好回调函数,然后在第二级生成二级树形菜单时回调这个外部函数。

此外还需要注意:本文在解决no.2关键点的时候,只能取得第三级选中的序号。而第一,第二级依然无法获取其序号。

main.xml源码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:orientation="vertical" android:layout_width="fill_parent"
 android:layout_height="fill_parent">
 <linearlayout android:id="@+id/linearlayout01"
 android:layout_width="wrap_content" android:layout_height="wrap_content">
 <button android:layout_height="wrap_content" android:text="两层结构"
  android:layout_width="160dip" android:id="@+id/btnnormal"></button>
 <button android:layout_height="wrap_content" android:text="三层结构"
  android:layout_width="160dip" android:id="@+id/btnsuper"></button>
 </linearlayout>
 <expandablelistview android:id="@+id/expandablelistview01"
 android:layout_width="fill_parent" android:layout_height="fill_parent"></expandablelistview>
</linearlayout>

testexpandablelist.java是主类,调用其他工具类,源码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package com.testexpandablelist;
import java.util.list;
import android.app.activity;
import android.os.bundle;
import android.util.log;
import android.view.view;
import android.widget.button;
import android.widget.expandablelistview;
import android.widget.expandablelistview.onchildclicklistener;
import android.widget.toast;
public class testexpandablelist extends activity {
  /** called when the activity is first created. */
 expandablelistview expandablelist;
 treeviewadapter adapter;
 supertreeviewadapter superadapter;
 button btnnormal,btnsuper;
  // sample data set. children[i] contains the children (string[]) for groups[i].
  public string[] groups = { "xxxx好友", "xxxx同学", "xxxxx女人"};
  public string[][] child= {
      { "a君", "b君", "c君", "d君" },
      { "同学甲", "同学乙", "同学丙"},
      { "御姐", "萝莉" }
  };
  public string[] parent = { "xxxx好友", "xxxx同学"};
  public string[][][] child_grandson= {
   {{"a君"},
    {"aa","aaa"}},
   {{"b君"},
    {"bbb","bbbb","bbbbb"}},
   {{"c君"},
    {"ccc","cccc"}},
   {{"d君"},
    {"ddd","dddd","ddddd"}},
  };
  @override
  public void oncreate(bundle savedinstancestate) {
    super.oncreate(savedinstancestate);
    setcontentview(r.layout.main);
    this.settitle("expandablelistview练习----hellogv");
    btnnormal=(button)this.findviewbyid(r.id.btnnormal);
    btnnormal.setonclicklistener(new clickevent());
    btnsuper=(button)this.findviewbyid(r.id.btnsuper);
    btnsuper.setonclicklistener(new clickevent());
    adapter=new treeviewadapter(this,treeviewadapter.paddingleft>>1);
    superadapter=new supertreeviewadapter(this,stvclickevent);
    expandablelist=(expandablelistview) testexpandablelist.this.findviewbyid(r.id.expandablelistview01);
  }
  class clickevent implements view.onclicklistener{
 @override
 public void onclick(view v) {
  adapter.removeall();
  adapter.notifydatasetchanged();
  superadapter.removeall();
  superadapter.notifydatasetchanged();
  
  if(v==btnnormal)
  {
     list<treeviewadapter.treenode> treenode = adapter.gettreenode();
     for(int i=0;i<groups.length;i++)
     {
      treeviewadapter.treenode node=new treeviewadapter.treenode();
      node.parent=groups[i];
      for(int ii=0;ii<child[i].length;ii++)
      {
      node.childs.add(child[i][ii]);
      }
      treenode.add(node);
     }
     adapter.updatetreenode(treenode);  
     expandablelist.setadapter(adapter);
     expandablelist.setonchildclicklistener(new onchildclicklistener(){
   @override
   public boolean onchildclick(expandablelistview arg0, view arg1,
   int parent, int children, long arg4) {
   string str="parent id:"+string.valueof(parent)+",children id:"+string.valueof(children);
   toast.maketext(testexpandablelist.this, str, 300).show();
   return false;
   }
     });
  }
  else if(v==btnsuper){
  list<supertreeviewadapter.supertreenode> supertreenode = superadapter.gettreenode();
     for(int i=0;i<parent.length;i++)//第一层
     {
      supertreeviewadapter.supertreenode supernode=new supertreeviewadapter.supertreenode();
      supernode.parent=parent[i];
      
      //第二层
      for(int ii=0;ii<child_grandson.length;ii++)
       {
        treeviewadapter.treenode node=new treeviewadapter.treenode();
        node.parent=child_grandson[ii][0][0];//第二级菜单的标题
        
        for(int iii=0;iii<child_grandson[ii][1].length;iii++)//第三级菜单
        {
        node.childs.add(child_grandson[ii][1][iii]);
        }
        supernode.childs.add(node);
       }
      supertreenode.add(supernode);
     }
     superadapter.updatetreenode(supertreenode);
     expandablelist.setadapter(superadapter);
  }
 }
  }
  /**
   * 三级树形菜单的事件不再可用,本函数由三级树形菜单的子项(二级菜单)进行回调
   */
  onchildclicklistener stvclickevent=new onchildclicklistener(){
 @override
 public boolean onchildclick(expandablelistview parent,
  view v, int groupposition, int childposition,
  long id) {
  string str="parent id:"+string.valueof(groupposition)+",children id:"+string.valueof(childposition);
  toast.maketext(testexpandablelist.this, str, 300).show();
  return false;
 }
  };
}

treeviewadapter.java是实现二级树形菜单的工具类,源码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package com.testexpandablelist;
import java.util.arraylist;
import java.util.list;
import android.content.context;
import android.util.log;
import android.view.gravity;
import android.view.view;
import android.view.viewgroup;
import android.widget.abslistview;
import android.widget.baseexpandablelistadapter;
import android.widget.textview;
public class treeviewadapter extends baseexpandablelistadapter{
 public static final int itemheight=48;//每项的高度
 public static final int paddingleft=36;//每项的高度
 private int mypaddingleft=0;//如果是由supertreeview调用,则作为子项需要往右移
 static public class treenode{
 object parent;
 list<object> childs=new arraylist<object>();
 }
 list<treenode> treenodes = new arraylist<treenode>();
 context parentcontext;
 public treeviewadapter(context view,int mypaddingleft)
 {
 parentcontext=view;
 this.mypaddingleft=mypaddingleft;
 }
 public list<treenode> gettreenode()
 {
 return treenodes;
 }
 public void updatetreenode(list<treenode> nodes)
 {
 treenodes=nodes;
 }
 public void removeall()
 {
 treenodes.clear();
 }
 public object getchild(int groupposition, int childposition) {
 return treenodes.get(groupposition).childs.get(childposition);
 }
 public int getchildrencount(int groupposition) {
 return treenodes.get(groupposition).childs.size();
 }
 static public textview gettextview(context context) {
 abslistview.layoutparams lp = new abslistview.layoutparams(
  viewgroup.layoutparams.fill_parent, itemheight);
 textview textview = new textview(context);
 textview.setlayoutparams(lp);
 textview.setgravity(gravity.center_vertical | gravity.left);
 return textview;
 }
 public view getchildview(int groupposition, int childposition,
  boolean islastchild, view convertview, viewgroup parent) {
 textview textview = gettextview(this.parentcontext);
 textview.settext(getchild(groupposition, childposition).tostring());
 textview.setpadding(mypaddingleft+paddingleft, 0, 0, 0);
 return textview;
 }
 public view getgroupview(int groupposition, boolean isexpanded,
  view convertview, viewgroup parent) {
 textview textview = gettextview(this.parentcontext);
 textview.settext(getgroup(groupposition).tostring());
 textview.setpadding(mypaddingleft+(paddingleft>>1), 0, 0, 0);
 return textview;
 }
 public long getchildid(int groupposition, int childposition) {
 return childposition;
 }
 public object getgroup(int groupposition) {
 return treenodes.get(groupposition).parent;
 }
 public int getgroupcount() {
 return treenodes.size();
 }
 public long getgroupid(int groupposition) {
 return groupposition;
 }
 public boolean ischildselectable(int groupposition, int childposition) {
 return true;
 }
 public boolean hasstableids() {
 return true;
 }
}

supertreeviewadapter.java是实现三级树形菜单的工具类,会用到treeviewadapter.java,源码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package com.testexpandablelist;
import java.util.arraylist;
import java.util.list;
import com.testexpandablelist.treeviewadapter.treenode;
import android.content.context;
import android.view.view;
import android.view.viewgroup;
import android.widget.abslistview;
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.textview;
public class supertreeviewadapter extends baseexpandablelistadapter {
 static public class supertreenode {
 object parent;
 //二级树形菜单的结构体
 list<treeviewadapter.treenode> childs = new arraylist<treeviewadapter.treenode>();
 }
 private list<supertreenode> supertreenodes = new arraylist<supertreenode>();
 private context parentcontext;
 private onchildclicklistener stvclickevent;//外部回调函数
 public supertreeviewadapter(context view,onchildclicklistener stvclickevent) {
 parentcontext = view;
 this.stvclickevent=stvclickevent;
 }
 public list<supertreenode> gettreenode() {
 return supertreenodes;
 }
 public void updatetreenode(list<supertreenode> node) {
 supertreenodes = node;
 }
 public void removeall()
 {
 supertreenodes.clear();
 }
 public object getchild(int groupposition, int childposition) {
 return supertreenodes.get(groupposition).childs.get(childposition);
 }
 public int getchildrencount(int groupposition) {
 return supertreenodes.get(groupposition).childs.size();
 }
 public expandablelistview getexpandablelistview() {
 abslistview.layoutparams lp = new abslistview.layoutparams(
  viewgroup.layoutparams.fill_parent, treeviewadapter.itemheight);
 expandablelistview supertreeview = new expandablelistview(parentcontext);
 supertreeview.setlayoutparams(lp);
 return supertreeview;
 }
 /**
 * 三层树结构中的第二层是一个expandablelistview
 */
 public view getchildview(int groupposition, int childposition,
  boolean islastchild, view convertview, viewgroup parent) {
 // 是
 final expandablelistview treeview = getexpandablelistview();
 final treeviewadapter treeviewadapter = new treeviewadapter(this.parentcontext,0);
 list<treenode> tmp = treeviewadapter.gettreenode();//临时变量取得treeviewadapter的treenode集合,可为空
 final treenode treenode=(treenode) getchild(groupposition, childposition);
 tmp.add(treenode);
 treeviewadapter.updatetreenode(tmp);
 treeview.setadapter(treeviewadapter);
 //关键点:取得选中的二级树形菜单的父子节点,结果返回给外部回调函数
 treeview.setonchildclicklistener(this.stvclickevent);
 /**
  * 关键点:第二级菜单展开时通过取得节点数来设置第三级菜单的大小
  */
 treeview.setongroupexpandlistener(new ongroupexpandlistener() {
  @override
  public void ongroupexpand(int groupposition) {
  abslistview.layoutparams lp = new abslistview.layoutparams(
   viewgroup.layoutparams.fill_parent,
   (treenode.childs.size()+1)*treeviewadapter.itemheight + 10);
  treeview.setlayoutparams(lp);
  }
 });
 /**
  * 第二级菜单回收时设置为标准item大小
  */
 treeview.setongroupcollapselistener(new ongroupcollapselistener() {
  @override
  public void ongroupcollapse(int groupposition) {
  
  abslistview.layoutparams lp = new abslistview.layoutparams(viewgroup.layoutparams.fill_parent,
   treeviewadapter.itemheight);
  treeview.setlayoutparams(lp);
  }
 });
 treeview.setpadding(treeviewadapter.paddingleft, 0, 0, 0);
 return treeview;
 }
 /**
 * 三级树结构中的首层是textview,用于作为title
 */
 public view getgroupview(int groupposition, boolean isexpanded,
  view convertview, viewgroup parent) {
 textview textview = treeviewadapter.gettextview(this.parentcontext);
 textview.settext(getgroup(groupposition).tostring());
 textview.setpadding(treeviewadapter.paddingleft, 0, 0, 0);
 return textview;
 }
 public long getchildid(int groupposition, int childposition) {
 return childposition;
 }
 public object getgroup(int groupposition) {
 return supertreenodes.get(groupposition).parent;
 }
 public int getgroupcount() {
 return supertreenodes.size();
 }
 public long getgroupid(int groupposition) {
 return groupposition;
 }
 public boolean ischildselectable(int groupposition, int childposition) {
 return true;
 }
 public boolean hasstableids() {
 return true;
 }
}

总结,使用expandablelist实现三级树形菜单时有些bug不好解决,而且定义三维数组的时候也要倍加小心!所以尽量把数据化简来使用二级树形菜单。

希望本文所述代码对大家进行android程序设计有一定的帮助。