一般来说在android里要实现树形菜单,都是用expandablelist(也有高手自己继承listview或者linearlayout来做),但是expandablelist一般只能实现2级树形菜单。本文所述实例也依然使用expandablelist,但是要实现的是3级树形菜单。
本文程序运行效果图如下图所示:
当用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程序设计有一定的帮助。