Android 多级菜单实现


摘要:   在Android里要实现树形菜单,都是用ExpandableList,但是ExpandableList一般只能实现2级树形菜单......本文也依然使用ExpandableList,但是要实现的是3级树形菜单。程序运行效果图:              ...





Android里要实现树形菜单,都是用ExpandableList,但是ExpandableList一般只能实现2级树形菜单......本文也依然使用ExpandableList,但是要实现的是3级树形菜单。程序运行效果图:

 


 

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




View Code



static public class TreeNode{ 
Object parent; 
List




 

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


View Code


static public class SuperTreeNode { 
Object parent; 
//二级树形菜单的结构体 
List childs = new ArrayList(); 
}




 

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



<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 
android:layout_height="wrap_content" 
android:text="三层结构" 
android:layout_width="160dip" 
android:id="@+id/btnSuper"> 
 
<expandablelistview android:id="@+id/ExpandableListView01" 
android:layout_width="fill_parent" 
android:layout_height="fill_parent">





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


View Code


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 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 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; 
} 

}; 
}




 

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


View Code


package eoe.demo; 

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




  实现**树形菜单的工具类


View Code


package eoe.demo; 

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 childs = new ArrayList(); 
} 

private List superTreeNodes = new ArrayList(); 
private Context parentContext; 
private OnChildClickListener stvClickEvent;//外部回调函数 

public SuperTreeViewAdapter(Context view,OnChildClickListener stvClickEvent) { 
parentContext = view; 
this.stvClickEvent=stvClickEvent; 
} 

public List GetTreeNode() { 
return superTreeNodes; 
} 

public void UpdateTreeNode(List 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 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; 
} 
}




Android里要实现树形菜单,都是用ExpandableList,但是ExpandableList一般只能实现2级树形菜单......本文也依然使用ExpandableList,但是要实现的是3级树形菜单。程序运行效果图:

 

                              

 

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




View Code


static public class TreeNode{ 
Object parent; 
List


 

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




View Code


static public class SuperTreeNode { 
Object parent; 
//二级树形菜单的结构体 
List childs = new ArrayList(); 
}


 

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



<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 
android:layout_height="wrap_content" 
android:text="三层结构" 
android:layout_width="160dip" 
android:id="@+id/btnSuper"> 
 
<expandablelistview android:id="@+id/ExpandableListView01" 
android:layout_width="fill_parent" 
android:layout_height="fill_parent">




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




View Code


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 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 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; 
} 

}; 
}


 

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




View Code


package eoe.demo; 

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


  实现**树形菜单的工具类



View Code


package eoe.demo; 

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 childs = new ArrayList(); 
} 

private List superTreeNodes = new ArrayList(); 
private Context parentContext; 
private OnChildClickListener stvClickEvent;//外部回调函数 

public SuperTreeViewAdapter(Context view,OnChildClickListener stvClickEvent) { 
parentContext = view; 
this.stvClickEvent=stvClickEvent; 
} 

public List GetTreeNode() { 
return superTreeNodes; 
} 

public void UpdateTreeNode(List 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 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; 
} 
}