Android提高十七篇之多级树形菜单的实现

本文来自http://blog.csdn.net/hellogv/,引用必须注明出处!

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

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

  1. staticpublicclassTreeNode{
  2. Objectparent;
  3. List<Object>childs=newArrayList<Object>();
  4. }

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

  1. staticpublicclassSuperTreeNode{
  2. Objectparent;
  3. //二级树形菜单的结构体
  4. List<TreeViewAdapter.TreeNode>childs=newArrayList<TreeViewAdapter.TreeNode>();
  5. }

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

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

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

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

main.xml源码如下:

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

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

  1. packagecom.testExpandableList;
  2. importjava.util.List;
  3. importandroid.app.Activity;
  4. importandroid.os.Bundle;
  5. importandroid.util.Log;
  6. importandroid.view.View;
  7. importandroid.widget.Button;
  8. importandroid.widget.ExpandableListView;
  9. importandroid.widget.ExpandableListView.OnChildClickListener;
  10. importandroid.widget.Toast;
  11. publicclasstestExpandableListextendsActivity{
  12. /**Calledwhentheactivityisfirstcreated.*/
  13. ExpandableListViewexpandableList;
  14. TreeViewAdapteradapter;
  15. SuperTreeViewAdaptersuperAdapter;
  16. ButtonbtnNormal,btnSuper;
  17. //Sampledataset.children[i]containsthechildren(String[])forgroups[i].
  18. publicString[]groups={"xxxx好友","xxxx同学","xxxxx女人"};
  19. publicString[][]child={
  20. {"A君","B君","C君","D君"},
  21. {"同学甲","同学乙","同学丙"},
  22. {"御姐","萝莉"}
  23. };
  24. publicString[]parent={"xxxx好友","xxxx同学"};
  25. publicString[][][]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. publicvoidonCreate(BundlesavedInstanceState){
  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(newClickEvent());
  42. btnSuper=(Button)this.findViewById(R.id.btnSuper);
  43. btnSuper.setOnClickListener(newClickEvent());
  44. adapter=newTreeViewAdapter(this,TreeViewAdapter.PaddingLeft>>1);
  45. superAdapter=newSuperTreeViewAdapter(this,stvClickEvent);
  46. expandableList=(ExpandableListView)testExpandableList.this.findViewById(R.id.ExpandableListView01);
  47. }
  48. classClickEventimplementsView.OnClickListener{
  49. @Override
  50. publicvoidonClick(Viewv){
  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(inti=0;i<groups.length;i++)
  59. {
  60. TreeViewAdapter.TreeNodenode=newTreeViewAdapter.TreeNode();
  61. node.parent=groups[i];
  62. for(intii=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(newOnChildClickListener(){
  71. @Override
  72. publicbooleanonChildClick(ExpandableListViewarg0,Viewarg1,
  73. intparent,intchildren,longarg4){
  74. Stringstr="parentid:"+String.valueOf(parent)+",childrenid:"+String.valueOf(children);
  75. Toast.makeText(testExpandableList.this,str,300).show();
  76. returnfalse;
  77. }
  78. });
  79. }
  80. elseif(v==btnSuper){
  81. List<SuperTreeViewAdapter.SuperTreeNode>superTreeNode=superAdapter.GetTreeNode();
  82. for(inti=0;i<parent.length;i++)//第一层
  83. {
  84. SuperTreeViewAdapter.SuperTreeNodesuperNode=newSuperTreeViewAdapter.SuperTreeNode();
  85. superNode.parent=parent[i];
  86. //第二层
  87. for(intii=0;ii<child_grandson.length;ii++)
  88. {
  89. TreeViewAdapter.TreeNodenode=newTreeViewAdapter.TreeNode();
  90. node.parent=child_grandson[ii][0][0];//第二级菜单的标题
  91. for(intiii=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. OnChildClickListenerstvClickEvent=newOnChildClickListener(){
  108. @Override
  109. publicbooleanonChildClick(ExpandableListViewparent,
  110. Viewv,intgroupPosition,intchildPosition,
  111. longid){
  112. Stringstr="parentid:"+String.valueOf(groupPosition)+",childrenid:"+String.valueOf(childPosition);
  113. Toast.makeText(testExpandableList.this,str,300).show();
  114. returnfalse;
  115. }
  116. };
  117. }

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

  1. packagecom.testExpandableList;
  2. importjava.util.ArrayList;
  3. importjava.util.List;
  4. importandroid.content.Context;
  5. importandroid.util.Log;
  6. importandroid.view.Gravity;
  7. importandroid.view.View;
  8. importandroid.view.ViewGroup;
  9. importandroid.widget.AbsListView;
  10. importandroid.widget.BaseExpandableListAdapter;
  11. importandroid.widget.TextView;
  12. publicclassTreeViewAdapterextendsBaseExpandableListAdapter{
  13. publicstaticfinalintItemHeight=48;//每项的高度
  14. publicstaticfinalintPaddingLeft=36;//每项的高度
  15. privateintmyPaddingLeft=0;//如果是由SuperTreeView调用,则作为子项需要往右移
  16. staticpublicclassTreeNode{
  17. Objectparent;
  18. List<Object>childs=newArrayList<Object>();
  19. }
  20. List<TreeNode>treeNodes=newArrayList<TreeNode>();
  21. ContextparentContext;
  22. publicTreeViewAdapter(Contextview,intmyPaddingLeft)
  23. {
  24. parentContext=view;
  25. this.myPaddingLeft=myPaddingLeft;
  26. }
  27. publicList<TreeNode>GetTreeNode()
  28. {
  29. returntreeNodes;
  30. }
  31. publicvoidUpdateTreeNode(List<TreeNode>nodes)
  32. {
  33. treeNodes=nodes;
  34. }
  35. publicvoidRemoveAll()
  36. {
  37. treeNodes.clear();
  38. }
  39. publicObjectgetChild(intgroupPosition,intchildPosition){
  40. returntreeNodes.get(groupPosition).childs.get(childPosition);
  41. }
  42. publicintgetChildrenCount(intgroupPosition){
  43. returntreeNodes.get(groupPosition).childs.size();
  44. }
  45. staticpublicTextViewgetTextView(Contextcontext){
  46. AbsListView.LayoutParamslp=newAbsListView.LayoutParams(
  47. ViewGroup.LayoutParams.FILL_PARENT,ItemHeight);
  48. TextViewtextView=newTextView(context);
  49. textView.setLayoutParams(lp);
  50. textView.setGravity(Gravity.CENTER_VERTICAL|Gravity.LEFT);
  51. returntextView;
  52. }
  53. publicViewgetChildView(intgroupPosition,intchildPosition,
  54. booleanisLastChild,ViewconvertView,ViewGroupparent){
  55. TextViewtextView=getTextView(this.parentContext);
  56. textView.setText(getChild(groupPosition,childPosition).toString());
  57. textView.setPadding(myPaddingLeft+PaddingLeft,0,0,0);
  58. returntextView;
  59. }
  60. publicViewgetGroupView(intgroupPosition,booleanisExpanded,
  61. ViewconvertView,ViewGroupparent){
  62. TextViewtextView=getTextView(this.parentContext);
  63. textView.setText(getGroup(groupPosition).toString());
  64. textView.setPadding(myPaddingLeft+(PaddingLeft>>1),0,0,0);
  65. returntextView;
  66. }
  67. publiclonggetChildId(intgroupPosition,intchildPosition){
  68. returnchildPosition;
  69. }
  70. publicObjectgetGroup(intgroupPosition){
  71. returntreeNodes.get(groupPosition).parent;
  72. }
  73. publicintgetGroupCount(){
  74. returntreeNodes.size();
  75. }
  76. publiclonggetGroupId(intgroupPosition){
  77. returngroupPosition;
  78. }
  79. publicbooleanisChildSelectable(intgroupPosition,intchildPosition){
  80. returntrue;
  81. }
  82. publicbooleanhasStableIds(){
  83. returntrue;
  84. }
  85. }

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

  1. packagecom.testExpandableList;
  2. importjava.util.ArrayList;
  3. importjava.util.List;
  4. importcom.testExpandableList.TreeViewAdapter.TreeNode;
  5. importandroid.content.Context;
  6. importandroid.view.View;
  7. importandroid.view.ViewGroup;
  8. importandroid.widget.AbsListView;
  9. importandroid.widget.BaseExpandableListAdapter;
  10. importandroid.widget.ExpandableListView;
  11. importandroid.widget.ExpandableListView.OnChildClickListener;
  12. importandroid.widget.ExpandableListView.OnGroupCollapseListener;
  13. importandroid.widget.ExpandableListView.OnGroupExpandListener;
  14. importandroid.widget.TextView;
  15. publicclassSuperTreeViewAdapterextendsBaseExpandableListAdapter{
  16. staticpublicclassSuperTreeNode{
  17. Objectparent;
  18. //二级树形菜单的结构体
  19. List<TreeViewAdapter.TreeNode>childs=newArrayList<TreeViewAdapter.TreeNode>();
  20. }
  21. privateList<SuperTreeNode>superTreeNodes=newArrayList<SuperTreeNode>();
  22. privateContextparentContext;
  23. privateOnChildClickListenerstvClickEvent;//外部回调函数
  24. publicSuperTreeViewAdapter(Contextview,OnChildClickListenerstvClickEvent){
  25. parentContext=view;
  26. this.stvClickEvent=stvClickEvent;
  27. }
  28. publicList<SuperTreeNode>GetTreeNode(){
  29. returnsuperTreeNodes;
  30. }
  31. publicvoidUpdateTreeNode(List<SuperTreeNode>node){
  32. superTreeNodes=node;
  33. }
  34. publicvoidRemoveAll()
  35. {
  36. superTreeNodes.clear();
  37. }
  38. publicObjectgetChild(intgroupPosition,intchildPosition){
  39. returnsuperTreeNodes.get(groupPosition).childs.get(childPosition);
  40. }
  41. publicintgetChildrenCount(intgroupPosition){
  42. returnsuperTreeNodes.get(groupPosition).childs.size();
  43. }
  44. publicExpandableListViewgetExpandableListView(){
  45. AbsListView.LayoutParamslp=newAbsListView.LayoutParams(
  46. ViewGroup.LayoutParams.FILL_PARENT,TreeViewAdapter.ItemHeight);
  47. ExpandableListViewsuperTreeView=newExpandableListView(parentContext);
  48. superTreeView.setLayoutParams(lp);
  49. returnsuperTreeView;
  50. }
  51. /**
  52. *三层树结构中的第二层是一个ExpandableListView
  53. */
  54. publicViewgetChildView(intgroupPosition,intchildPosition,
  55. booleanisLastChild,ViewconvertView,ViewGroupparent){
  56. //是
  57. finalExpandableListViewtreeView=getExpandableListView();
  58. finalTreeViewAdaptertreeViewAdapter=newTreeViewAdapter(this.parentContext,0);
  59. List<TreeNode>tmp=treeViewAdapter.GetTreeNode();//临时变量取得TreeViewAdapter的TreeNode集合,可为空
  60. finalTreeNodetreeNode=(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(newOnGroupExpandListener(){
  70. @Override
  71. publicvoidonGroupExpand(intgroupPosition){
  72. AbsListView.LayoutParamslp=newAbsListView.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(newOnGroupCollapseListener(){
  82. @Override
  83. publicvoidonGroupCollapse(intgroupPosition){
  84. AbsListView.LayoutParamslp=newAbsListView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
  85. TreeViewAdapter.ItemHeight);
  86. treeView.setLayoutParams(lp);
  87. }
  88. });
  89. treeView.setPadding(TreeViewAdapter.PaddingLeft,0,0,0);
  90. returntreeView;
  91. }
  92. /**
  93. *三级树结构中的首层是TextView,用于作为title
  94. */
  95. publicViewgetGroupView(intgroupPosition,booleanisExpanded,
  96. ViewconvertView,ViewGroupparent){
  97. TextViewtextView=TreeViewAdapter.getTextView(this.parentContext);
  98. textView.setText(getGroup(groupPosition).toString());
  99. textView.setPadding(TreeViewAdapter.PaddingLeft,0,0,0);
  100. returntextView;
  101. }
  102. publiclonggetChildId(intgroupPosition,intchildPosition){
  103. returnchildPosition;
  104. }
  105. publicObjectgetGroup(intgroupPosition){
  106. returnsuperTreeNodes.get(groupPosition).parent;
  107. }
  108. publicintgetGroupCount(){
  109. returnsuperTreeNodes.size();
  110. }
  111. publiclonggetGroupId(intgroupPosition){
  112. returngroupPosition;
  113. }
  114. publicbooleanisChildSelectable(intgroupPosition,intchildPosition){
  115. returntrue;
  116. }
  117. publicbooleanhasStableIds(){
  118. returntrue;
  119. }
  120. }

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

你可能感兴趣的:(android)