android下ExpandableListView的详细使用demo

本文介绍如何使用 Android 中的 ExpandableListView 控件实现多级联动菜单效果。通过自定义 SuperTreeViewAdapter 和 TreeViewAdapter,可以创建复杂的树形结构,并支持动态更新和点击事件。适用于需要展示多层次数据的应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一直认为ExpandableListView是比较好的一个东西,虽然不常用,但是上次网上查资料看到了这个demo,研究了一下,标记一下,以后使用的时候来看看。


效果图如下:



ExpandableListViewActivity

package com.yhx.expandablelistview;

import java.util.List;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ExpandableListView;
import android.widget.Toast;
import android.widget.ExpandableListView.OnChildClickListener;

//主activity
public class ExpandableListViewActivity extends Activity {

	ExpandableListView expandableListView;
	TreeViewAdapter adapter;
	SuperTreeViewAdapter superAdapter;
	Button double_btn, treble_btn;

	public String[] groups = { "friends", "family" };
	public String[][] childs = { { "A", "AA", "AAA" }, { "B", "BB", "BBB" } };

	public String[] parent = { "boys", "girls" };
	public String[][][] child_grandchild = { { { "Toms" }, { "A", "AA" } },
			{ { "Kelly" }, { "B", "BBB" } } };

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		double_btn = (Button) findViewById(R.id.button1);
		treble_btn = (Button) findViewById(R.id.button2);
		expandableListView = (ExpandableListView) findViewById(R.id.expandablelistview);
		double_btn.setOnClickListener(listener);
		treble_btn.setOnClickListener(listener);
		adapter = new TreeViewAdapter(this, 38);
		superAdapter = new SuperTreeViewAdapter(this, stvClickEvent);
	}

	public OnClickListener listener = new OnClickListener() {

		@Override
		public void onClick(View v) {
			// TODO Auto-generated method stub
			adapter.removeAll();
			adapter.notifyDataSetChanged();
			superAdapter.RemoveAll();
			superAdapter.notifyDataSetChanged();
			if (v == double_btn) {
				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 j = 0; j < childs[i].length; j++) {
						node.childs.add(childs[i][j]);
					}
					treeNode.add(node);
				}
				adapter.updateTreeNode(treeNode);
				expandableListView.setAdapter(adapter);
				expandableListView
						.setOnChildClickListener(new OnChildClickListener() {

							@Override
							public boolean onChildClick(
									ExpandableListView parent, View v,
									int groupPosition, int childPosition,
									long id) {
								String str = "parent_id = " + groupPosition
										+ " child_id = " + childPosition;
								Toast.makeText(ExpandableListViewActivity.this,
										str, Toast.LENGTH_SHORT).show();
								return false;
							}
						});
			} else if (v == treble_btn) {
				List<SuperTreeViewAdapter.SuperTreeNode> superNodeTree = superAdapter
						.GetTreeNode();
				for (int i = 0; i < parent.length; i++) {
					SuperTreeViewAdapter.SuperTreeNode superNode = new SuperTreeViewAdapter.SuperTreeNode();
					superNode.parent = parent[i];

					for (int j = 0; j < child_grandchild.length; j++) {
						TreeViewAdapter.TreeNode node = new TreeViewAdapter.TreeNode();
						node.parent = child_grandchild[j][0][0];
						for (int k = 0; k < child_grandchild[j][1].length; k++) {
							node.childs.add(child_grandchild[j][1][k]);
						}
						superNode.childs.add(node);
					}
					superNodeTree.add(superNode);
				}
				superAdapter.UpdateTreeNode(superNodeTree);
				expandableListView.setAdapter(superAdapter);
			}
		}

	};

	OnChildClickListener stvClickEvent = new OnChildClickListener() {

		@Override
		public boolean onChildClick(ExpandableListView parent, View v,
				int groupPosition, int childPosition, long id) {
			// TODO Auto-generated method stub
			String msg = "parent_id = " + groupPosition + " child_id = "
					+ childPosition;
			Toast.makeText(ExpandableListViewActivity.this, msg,
					Toast.LENGTH_SHORT).show();
			return false;
		}
	};
}


SuperTreeViewAdapter

package com.yhx.expandablelistview;

import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import com.yhx.expandablelistview.TreeViewAdapter.TreeNode;
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*2, 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*2, 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;
	}
}

TreeViewAdapter


package com.yhx.expandablelistview;

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
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 = 38;
	private int myPaddingLeft = 0;

	static public class TreeNode {
		Object parent;
		List<Object> childs = new ArrayList<Object>();
	}

	List<TreeNode> treeNodes = new ArrayList<TreeNode>();
	Context parentContext;

	public TreeViewAdapter(Context context, int myPaddingLeft) {
		parentContext = context;
		this.myPaddingLeft = myPaddingLeft;

	}

	public List<TreeNode> getTreeNode() {
		return treeNodes;
	}

	public void updateTreeNode(List<TreeNode> nodes) {
		treeNodes = nodes;
	}

	public void removeAll() {
		treeNodes.clear();
	}

	@Override
	public Object getChild(int groupPosition, int childPosition) {
		// TODO Auto-generated method stub
		return treeNodes.get(groupPosition).childs.get(childPosition);
	}

	@Override
	public long getChildId(int groupPosition, int childPosition) {
		// TODO Auto-generated method stub
		return childPosition;
	}

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

	@Override
	public View getChildView(int groupPosition, int childPosition,
			boolean isLastChild, View convertView, ViewGroup parent) {
		// TODO Auto-generated method stub
		TextView textView = getTextView(this.parentContext);
		textView.setText(getChild(groupPosition, childPosition).toString());
		textView.setPadding(myPaddingLeft + PaddingLeft, 0, 0, 0);
		return textView;
	}

	@Override
	public int getChildrenCount(int groupPosition) {
		// TODO Auto-generated method stub
		return treeNodes.get(groupPosition).childs.size();
	}

	@Override
	public Object getGroup(int groupPosition) {
		// TODO Auto-generated method stub
		return treeNodes.get(groupPosition).parent;
	}

	@Override
	public int getGroupCount() {
		// TODO Auto-generated method stub
		return treeNodes.size();
	}

	@Override
	public long getGroupId(int groupPosition) {
		// TODO Auto-generated method stub
		return groupPosition;
	}

	@Override
	public View getGroupView(int groupPosition, boolean isExpanded,
			View convertView, ViewGroup parent) {
		// TODO Auto-generated method stub
		TextView textView = getTextView(this.parentContext);
		textView.setText(getGroup(groupPosition).toString());
		textView.setPadding(myPaddingLeft + (PaddingLeft >> 1), 0, 0, 0);
		return textView;
	}

	@Override
	public boolean hasStableIds() {
		// TODO Auto-generated method stub
		return true;
	}

	@Override
	public boolean isChildSelectable(int groupPosition, int childPosition) {
		// TODO Auto-generated method stub
		return true;
	}

}

SuperTreeNode

package com.yhx.expandablelistview;

import java.util.ArrayList;
import java.util.List;

public class SuperTreeNode {
	public Object parent;
	public List<TreeViewAdapter.TreeNode> childs = new ArrayList<TreeViewAdapter.TreeNode>();
}



TreeNode


package com.yhx.expandablelistview;

import java.util.ArrayList;
import java.util.List;

public class TreeNode {

	public Object parent;
	public List<Object> childs = new ArrayList<Object>();
}

完整的项目下载地址:点击这里


有时候,使用ListView并不能满足应用程序所需要的功能。有些应用程序需要多组ListView,这时候我们就要使用一种新的控件ExpandableListView——可以扩展的ListView。它的作用就是将ListView进行分组。就好像我们使用QQ的时候,有“我的好友”,“陌生人”,“黑名单”一样,点击一下会扩展开,再点击一下又会收缩回去。 ExpandableListView是一个垂直滚动显示两级列表项的视图,与ListView不同的是,它可以有两层:每一层都能够被独立的展开并显示其子项。这些子项来自于与该视图关联的ExpandableListAdapter。 每一个可以扩展的列表项的旁边都有一个指示符(箭头)用来说明该列表项目前的状态(这些状态一般是已经扩展开的列表项,还没有扩展开的列表项,子列表项和最后一个子列表项)。可以使用方法:setChildIndicator(Drawable),setGroupIndicator(Drawable)(或者相应的XML文件的属性) 去设置这些指示符的样式。当然也可以使用默认的指示符。布android.R.layout.simple_expandable_list_item_1,android.R.layout.simple_expandable_list_item_2 和ListView一样,ExpandableListView也是一个需要Adapter作为桥梁来取得数据的控件。一般适用于ExpandableListView的Adapter都要继承BaseExpandableListAdapter这个类,并且必须重载getGroupView和getChildView这两个最为重要的方法。 BaseExpandableListAdapter的主要重载方法如下: public abstract ObjectgetChild (int groupPosition, int childPosition) 取得与指定分组、指定子项目关联的数据. 参数 groupPosition 包含子视图的分组的位置. childPosition 指定的分组中的子视图的位置. 返回 与子视图关联的数据. public abstract long getChildId (int groupPosition, intchildPosition) 取得给定分组中给定子视图的ID. 该组ID必须在组中是唯一的.必须不同于其他所有ID(分组及子项目的ID). 参数 groupPosition 包含子视图的分组的位置. childPosition 要取得ID的指定的分组中的子视图的位置. 返回 与子视图关联的ID. public abstract View getChildView (int groupPosition, intchildPosition, boolean isLastChild, View convertView, ViewGroup parent) 取得显示给定分组给定子位置的数据用的视图. 参数 groupPosition 包含要取得子视图的分组位置. childPosition 分组中子视图(要返回的视图)的位置. isLastChild 该视图是否为组中的最后一个视图. convertView 如果可能,重用旧的视图对象.使用前你应该保证视图对象为非空,并且是否是合适的类型.如果该对象不能转换为可以正确显示数据的视图,该方法就创建新视图.不保证使用先前由 getChildView(int, int,boolean, View, ViewGroup)创建的视图. parent 该视图最终从属的父视图. 返回 指定位置相应的子视图. public abstract int getChildrenCount (int groupPosition) 取得指定分组的子元素数. 参数 groupPosition 要取得子元素个数的分组位置. 返回 指定分组的子元素个数. public abstract long getCombinedChildId (long groupId, long childId) 取得一览中可以唯一识别子条目的 ID(包括分组ID和子条目ID).可扩展列表要求每个条目 (分组条目和子条目)具有一个可以唯一识别列表中子条目和分组条目的ID. 该方法根据给定子条目ID和分组条目ID返回唯一识别ID.另外,如果 hasStableIds() 为真,该函数返回的ID必须是固定不变的. 参数 groupId 包含子条目ID的分组条目ID. childId 子条目的ID. 返回 可以在所有分组条目和子条目中唯一识别该子条目的ID(可能是固定不变
ExpandableListView实例(一)_数据库增删改查处理和listitem点击长按处理 本例说明: 1.实例中表现层与数据处理层分开,代码可复用性强,如果能看懂代码对算法会有提高. 2.组和子条目上"点击"事件处理,能够区分操作的是组还是子条目,并且得到组和子条目的内容. 3.组和子条目上"长按"事件处理,能够区分组和子条目,并且得到组和子条目的内容. 4.自定义条目样式,灵活与数据库中字段绑定. 5.实现对DB的增删改查,并且操作后自动刷新. 6.使用数据库处理框架AHibernate灵活操作sqlite数据库,详见: http://blog.csdn.net/lk_blog/article/details/7455992 ExpandableListView实例(二)_两种方式实现QQ中组后面显示子条目数量效果 本例说明: QQ,飞信等聊天工具中组后面后会显示有多少个子条目,这个是如何实现的呢?查阅了网上还没有相关的介绍,现在本文介绍两种方式实现此功能. 第一种方式:自定义Adapter,重写getGroupView方法. 第二种方式:自定义group.xml中的控件,加一个textview用于显示子条目个数. 注:本文数据库处理使用框架AHibernate,可以灵活操作sqlite数据库, 详见: http://blog.csdn.net/lk_blog/article/details/7455992 ExpandableListView实例(三)_实现QQ中"未分组"效果和"未分组"不可编辑删除功能 本例说明: 实现QQ中"未分组"效果和"未分组"不可编辑删除功能. 注:本文数据库处理使用框架AHibernate,可以灵活操作sqlite数据库, 详见: http://blog.csdn.net/lk_blog/article/details/7455992
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值