Android 多级树形菜单

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

Android 多级树形菜单

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

 
  1. static public class TreeNode{
  2. Object parent;
  3. List<Object> childs=new ArrayList<Object>();
  4. }

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

 
  1. static public class SuperTreeNode {
  2. Object parent;
  3. //二级树形菜单的结构体
  4. List<TreeViewAdapter.TreeNode> childs = new ArrayList<TreeViewAdapter.TreeNode>();
  5. }

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

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

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

PS:本文在解决No.2关键点的时候,只能取得第三级选中的序号.....而第一,第二级依然无法获取其序号。

main.xml源码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:orientation="vertical" android:layout_width="fill_parent"
  4. android:layout_height="fill_parent">
  5. <LinearLayout android:id="@+id/LinearLayout01"
  6. android:layout_width="wrap_content" android:layout_height="wrap_content">
  7. <Button android:layout_height="wrap_content" android:text="两层结构"
  8. android:layout_width="160dip" android:id="@+id/btnNormal"></Button>
  9. <Button android:layout_height="wrap_content" android:text="三层结构"
  10. android:layout_width="160dip" android:id="@+id/btnSuper"></Button>
  11. </LinearLayout>
  12. <ExpandableListView android:id="@+id/ExpandableListView01"
  13. android:layout_width="fill_parent" android:layout_height="fill_parent"></ExpandableListView>
  14. </LinearLayout>

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

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

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

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

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

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

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

上一篇:基于FS4412的DS18B20温度采集编程实现(1-时序分析)


下一篇:js中(function(){…})()立即执行函数写法理解(转载oschina)