在Android里要实现树形菜单,都是用ExpandableList(也有高手自己继承ListView或者LinearLayout来做),但是ExpandableList一般只能实现2级树形菜单......本文也依然使用ExpandableList,但是要实现的是3级树形菜单。本文程序运行效果图:

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

Java代码

1. static public class TreeNode{  
2.                Object parent;  
3. new ArrayList();  
4.        }

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

Java代码

1. static public class SuperTreeNode {  
2.                Object parent;  
3. //二级树形菜单的结构体  
4. new ArrayList[tr]();  
5.        }

       实现三级树形菜单有两点要注意的:1、第二级也是个树形菜单,因此必须在第二级项目展开/回收时设置足够的空间来完全显示二级树形菜单;2、在实现三级树形菜单时,发现菜单的方法都是用不了(如OnChildClickListener、OnGroupClickListener等),因此要获得选中的数据就必须在外部定义好回调函数,然后在第二级生成二级树形菜单时回调这个外部函数。PS:本文在解决No.2关键点的时候,只能取得第三级选中的序号.....而第一,第二级依然无法获取其序号。main.xml源码如下:

XML/HTML代码

1. <linearlayout xmlns:android="http://schemas.android.com/apk/res/android"   
2. android:orientation="vertical" android:layout_width="fill_parent"  
3. android:layout_height="fill_parent">  
4. <linearlayout android:id="@+id/LinearLayout01"   
5. android:layout_width="wrap_content" android:layout_height="wrap_content">  
6. <button android:layout_height="wrap_content" android:text="两层结构"   
7. android:layout_width="160dip" android:id="@+id/btnNormal">  
8. <button android:layout_height="wrap_content" android:text="三层结构"   
9. android:layout_width="160dip" android:id="@+id/btnSuper">  
10.           
11. <expandablelistview android:id="@+id/ExpandableListView01"   
12. android:layout_width="fill_parent" android:layout_height="fill_parent">

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

Java代码

1. package com.testExpandableList;  
2.   
3.   
4. import java.util.List;  
5. import android.app.Activity;  
6. import android.os.Bundle;  
7. import android.util.Log;  
8. import android.view.View;  
9. import android.widget.Button;  
10. import android.widget.ExpandableListView;  
11. import android.widget.ExpandableListView.OnChildClickListener;  
12. import android.widget.Toast;  
13.   
14. public class testExpandableList extends Activity {  
15. /** Called when the activity is first created. */  
16.         ExpandableListView expandableList;  
17.         TreeViewAdapter adapter;  
18.         SuperTreeViewAdapter superAdapter;  
19.         Button btnNormal,btnSuper;  
20. // Sample data set.  children[i] contains the children (String[]) for groups[i].  
21. public String[] groups = { "xxxx好友", "xxxx同学", "xxxxx女人"};  
22. public String[][]  child= {  
23. "A君", "B君", "C君", "D君" },  
24. "同学甲", "同学乙", "同学丙"},  
25. "御姐", "萝莉" }  
26.     };  
27.       
28. public String[] parent = { "xxxx好友", "xxxx同学"};  
29. public String[][][]  child_grandson= {  
30. "A君"},  
31. "AA","AAA"}},  
32. "B君"},  
33. "BBB","BBBB","BBBBB"}},  
34. "C君"},  
35. "CCC","CCCC"}},  
36. "D君"},  
37. "DDD","DDDD","DDDDD"}},  
38.     };  
39.       
40. @Override  
41. public void onCreate(Bundle savedInstanceState) {  
42. super.onCreate(savedInstanceState);  
43.         setContentView(R.layout.main);  
44. this.setTitle("ExpandableListView练习----hellogv");  
45. this.findViewById(R.id.btnNormal);  
46. new ClickEvent());  
47. this.findViewById(R.id.btnSuper);  
48. new ClickEvent());  
49. new TreeViewAdapter(this,TreeViewAdapter.PaddingLeft>>1);  
50. new SuperTreeViewAdapter(this,stvClickEvent);  
51. this.findViewById(R.id.ExpandableListView01);  
52.     }  
53.       
54. class ClickEvent implements View.OnClickListener{  
55.   
56. @Override  
57. public void onClick(View v) {  
58.                         adapter.RemoveAll();  
59.                         adapter.notifyDataSetChanged();  
60.                         superAdapter.RemoveAll();  
61.                         superAdapter.notifyDataSetChanged();  
62.                           
63. if(v==btnNormal)  
64.                         {  
65.                         List[tr] treeNode = adapter.GetTreeNode();  
66. for(int i=0;i<groups.length;i++)  
67.                         {  
68. new TreeViewAdapter.TreeNode();  
69.                                 node.parent=groups[i];  
70. for(int ii=0;ii<child[i].length;ii++)  
71.                                 {  
72.                                         node.childs.add(child[i][ii]);  
73.                                 }  
74.                                 treeNode.add(node);  
75.                         }  
76.                           
77.                         adapter.UpdateTreeNode(treeNode);       
78.                         expandableList.setAdapter(adapter);  
79. new OnChildClickListener(){  
80.   
81. @Override  
82. public boolean onChildClick(ExpandableListView arg0, View arg1,  
83. int parent, int children, long arg4) {  
84.                                                   
85. "parent id:"+String.valueOf(parent)+",children id:"+String.valueOf(children);  
86. this, str, 300).show();  
87. return false;  
88.                                         }  
89.                         });  
90.                         }  
91. else if(v==btnSuper){  
92.                                 List superTreeNode = superAdapter.GetTreeNode();  
93. for(int i=0;i<parent.length;i++) 第一层  
94.                         {  
95. new SuperTreeViewAdapter.SuperTreeNode();  
96.                                 superNode.parent=parent[i];  
97.                                   
98. //第二层  
99. for(int ii=0;ii<child_grandson.length;ii++)  
100.                             {  
101. new TreeViewAdapter.TreeNode();  
102. 0][0];//第二级菜单的标题  
103.                                       
104. for(int iii=0;iii<child_grandson[ii][1].length;iii++) 第三级菜单  
105.                                     {  
106. 1][iii]);  
107.                                     }  
108.                                     superNode.childs.add(node);  
109.                             }  
110.                                 superTreeNode.add(superNode);  
111.                                   
112.                         }  
113.                         superAdapter.UpdateTreeNode(superTreeNode);  
114.                         expandableList.setAdapter(superAdapter);  
115.                         }  
116.                 }  
117.     }  
118.   
119. /** 
120.      * 三级树形菜单的事件不再可用,本函数由三级树形菜单的子项(二级菜单)进行回调 
121.      */  
122. new OnChildClickListener(){  
123.   
124. @Override  
125. public boolean onChildClick(ExpandableListView parent,  
126. int groupPosition, int childPosition,  
127. long id) {  
128. "parent id:"+String.valueOf(groupPosition)+",children id:"+String.valueOf(childPosition);  
129. this, str, 300).show();  
130.                           
131. return false;  
132.                 }  
133.               
134.     };  
135. }

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

Java代码

1. package com.testExpandableList;  
2.   
3. import java.util.ArrayList;  
4. import java.util.List;  
5. import android.content.Context;  
6. import android.util.Log;  
7. import android.view.Gravity;  
8. import android.view.View;  
9. import android.view.ViewGroup;  
10. import android.widget.AbsListView;  
11. import android.widget.BaseExpandableListAdapter;  
12. import android.widget.TextView;  
13.   
14.   
15. public class TreeViewAdapter extends BaseExpandableListAdapter{  
16. public static final int ItemHeight=48;//每项的高度  
17. public static final int PaddingLeft=36;//每项的高度  
18. private int myPaddingLeft=0;//如果是由SuperTreeView调用,则作为子项需要往右移  
19.   
20. static public class TreeNode{  
21.                 Object parent;  
22. new ArrayList();  
23.         }  
24.           
25. new ArrayList[tr]();  
26.         Context parentContext;  
27.           
28. public TreeViewAdapter(Context view,int myPaddingLeft)  
29.         {  
30.                 parentContext=view;  
31. this.myPaddingLeft=myPaddingLeft;  
32.         }  
33.           
34. public List[tr] GetTreeNode()  
35.         {  
36. return treeNodes;  
37.         }  
38.           
39. public void UpdateTreeNode(List[tr] nodes)  
40.         {  
41.                 treeNodes=nodes;  
42.         }  
43.           
44. public void RemoveAll()  
45.         {  
46.                 treeNodes.clear();  
47.         }  
48.           
49. public Object getChild(int groupPosition, int childPosition) {  
50. return treeNodes.get(groupPosition).childs.get(childPosition);  
51.         }  
52.   
53. public int getChildrenCount(int groupPosition) {  
54. return treeNodes.get(groupPosition).childs.size();  
55.         }  
56.   
57. static public TextView getTextView(Context context) {  
58. new AbsListView.LayoutParams(  
59.                                 ViewGroup.LayoutParams.FILL_PARENT, ItemHeight);  
60.   
61. new TextView(context);  
62.                 textView.setLayoutParams(lp);  
63.                 textView.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);  
64. return textView;  
65.         }  
66.   
67. public View getChildView(int groupPosition, int childPosition,  
68. boolean isLastChild, View convertView, ViewGroup parent) {  
69. this.parentContext);  
70.                 textView.setText(getChild(groupPosition, childPosition).toString());  
71. 0, 0, 0);  
72. return textView;  
73.         }  
74.   
75. public View getGroupView(int groupPosition, boolean isExpanded,  
76.                         View convertView, ViewGroup parent) {  
77. this.parentContext);  
78.                 textView.setText(getGroup(groupPosition).toString());  
79. 1), 0, 0, 0);  
80. return textView;  
81.         }  
82.   
83. public long getChildId(int groupPosition, int childPosition) {  
84. return childPosition;  
85.         }  
86.   
87. public Object getGroup(int groupPosition) {  
88. return treeNodes.get(groupPosition).parent;  
89.         }  
90.   
91. public int getGroupCount() {  
92. return treeNodes.size();  
93.         }  
94.   
95. public long getGroupId(int groupPosition) {  
96. return groupPosition;  
97.         }  
98.   
99. public boolean isChildSelectable(int groupPosition, int childPosition) {  
100. return true;  
101.         }  
102.   
103. public boolean hasStableIds() {  
104. return true;  
105.         }  
106. }

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

Java代码

1. package com.testExpandableList;  
2.   
3. import java.util.ArrayList;  
4. import java.util.List;  
5. import com.testExpandableList.TreeViewAdapter.TreeNode;  
6. import android.content.Context;  
7. import android.view.View;  
8. import android.view.ViewGroup;  
9. import android.widget.AbsListView;  
10. import android.widget.BaseExpandableListAdapter;  
11. import android.widget.ExpandableListView;  
12. import android.widget.ExpandableListView.OnChildClickListener;  
13. import android.widget.ExpandableListView.OnGroupCollapseListener;  
14. import android.widget.ExpandableListView.OnGroupExpandListener;  
15. import android.widget.TextView;  
16.   
17. public class SuperTreeViewAdapter extends BaseExpandableListAdapter {  
18.   
19. static public class SuperTreeNode {  
20.                 Object parent;  
21. //二级树形菜单的结构体  
22. new ArrayList[tr]();  
23.         }  
24.   
25. private List superTreeNodes = new ArrayList();  
26. private Context parentContext;  
27. private OnChildClickListener stvClickEvent;//外部回调函数  
28.           
29. public SuperTreeViewAdapter(Context view,OnChildClickListener stvClickEvent) {  
30.                 parentContext = view;  
31. this.stvClickEvent=stvClickEvent;  
32.         }  
33.   
34. public List GetTreeNode() {  
35. return superTreeNodes;  
36.         }  
37.   
38. public void UpdateTreeNode(List node) {  
39.                 superTreeNodes = node;  
40.         }  
41.           
42. public void RemoveAll()  
43.         {  
44.                 superTreeNodes.clear();  
45.         }  
46.           
47. public Object getChild(int groupPosition, int childPosition) {  
48. return superTreeNodes.get(groupPosition).childs.get(childPosition);  
49.         }  
50.   
51. public int getChildrenCount(int groupPosition) {  
52. return superTreeNodes.get(groupPosition).childs.size();  
53.         }  
54.   
55. public ExpandableListView getExpandableListView() {  
56. new AbsListView.LayoutParams(  
57.                                 ViewGroup.LayoutParams.FILL_PARENT, TreeViewAdapter.ItemHeight);  
58. new ExpandableListView(parentContext);  
59.                 superTreeView.setLayoutParams(lp);  
60. return superTreeView;  
61.         }  
62.   
63. /** 
64.          * 三层树结构中的第二层是一个ExpandableListView 
65.          */          
66. public View getChildView(int groupPosition, int childPosition,  
67. boolean isLastChild, View convertView, ViewGroup parent) {  
68. // 是   
69. final ExpandableListView treeView = getExpandableListView();  
70. final TreeViewAdapter treeViewAdapter = new TreeViewAdapter(this.parentContext,0);  
71. //临时变量取得TreeViewAdapter的TreeNode集合,可为空  
72. final TreeNode treeNode=(TreeNode) getChild(groupPosition, childPosition);  
73.                 tmp.add(treeNode);  
74.                 treeViewAdapter.UpdateTreeNode(tmp);  
75.                 treeView.setAdapter(treeViewAdapter);  
76.                   
77. //关键点:取得选中的二级树形菜单的父子节点,结果返回给外部回调函数  
78. this.stvClickEvent);  
79.                   
80. /** 
81.                  * 关键点:第二级菜单展开时通过取得节点数来设置第三级菜单的大小 
82.                  */  
83. new OnGroupExpandListener() {  
84. @Override  
85. public void onGroupExpand(int groupPosition) {  
86.                                   
87. new AbsListView.LayoutParams(  
88.                                                 ViewGroup.LayoutParams.FILL_PARENT,  
89. 1)*TreeViewAdapter.ItemHeight + 10);  
90.                                 treeView.setLayoutParams(lp);  
91.                         }  
92.                 });  
93.                   
94. /** 
95.                  * 第二级菜单回收时设置为标准Item大小 
96.                  */  
97. new OnGroupCollapseListener() {  
98. @Override  
99. public void onGroupCollapse(int groupPosition) {  
100.                                   
101. new AbsListView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,  
102.                                                 TreeViewAdapter.ItemHeight);  
103.                                 treeView.setLayoutParams(lp);  
104.                         }  
105.                 });  
106. 0, 0, 0);  
107. return treeView;  
108.         }  
109.   
110. /** 
111.          * 三级树结构中的首层是TextView,用于作为title 
112.          */  
113. public View getGroupView(int groupPosition, boolean isExpanded,  
114.                         View convertView, ViewGroup parent) {  
115. this.parentContext);  
116.                 textView.setText(getGroup(groupPosition).toString());  
117. 0, 0, 0);  
118. return textView;  
119.         }  
120.   
121. public long getChildId(int groupPosition, int childPosition) {  
122. return childPosition;  
123.         }  
124.   
125. public Object getGroup(int groupPosition) {  
126. return superTreeNodes.get(groupPosition).parent;  
127.         }  
128.   
129. public int getGroupCount() {  
130. return superTreeNodes.size();  
131.         }  
132.   
133. public long getGroupId(int groupPosition) {  
134. return groupPosition;  
135.         }  
136.   
137. public boolean isChildSelectable(int groupPosition, int childPosition) {  
138. return true;  
139.         }  
140.   
141. public boolean hasStableIds() {  
142. return true;  
143.         }  
144. }

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