BaseExpandableListAdapter,二级列表的完全自定义(一)

本文介绍如何使用ExpandableListView创建二级列表,详细讲解了所需组件、适配器、数据源和布局设计。通过示例展示了表头和表体的布局,并预告在下篇将实现手势、点击事件等交互功能。

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

普通的ListView已经无法满足需要,下面来详细说明如何使用二级列表,二级列表有很多地方会使用到,如常见的QQ好友分组,效果图如下:


具体的内容包括表头和表体,点击表头会展开或合并表体,为方便表示,表头和表体均为图片加文字的布局。

分析完成二级列表需要的一些东西:

一:控件名称   ExpandableListView

二:适配器   继承自BaseExpandableListAdapter

三:数据源   以IT工作为例,把每组看成一个Object,所以数据源为List<Work>

四:子布局   包括表头和表体


下面完成每项的具体代码:

表头布局 work_groups,包括图片,表头名,指示器(有默认的指示器,但不同版本和手机表现不一)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    android:paddingBottom="5dp"
    android:paddingLeft="10dp"
    android:paddingRight="5dp"
    android:paddingTop="5dp" >

    <ImageView
        android:id="@+id/group_image1"
        android:layout_width="35dp"
        android:layout_height="35dp"
        android:layout_gravity="center_vertical" />

    <TextView
        android:id="@+id/group_title"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_weight="1"
        android:gravity="center"
        android:textColor="#9A32CD"
        android:textSize="18sp" />

    <ImageView
        android:id="@+id/group_image2"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_gravity="center_vertical" />

</LinearLayout>
表体布局 work_childs,包括图片和表体名

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    android:paddingBottom="5dp"
    android:paddingLeft="20dp"
    android:paddingRight="5dp"
    android:paddingTop="5dp" >

    <ImageView
        android:id="@+id/child_image1"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_gravity="center_vertical" />

    <TextView
        android:id="@+id/child_title"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_weight="1"
        android:gravity="center"
        android:textColor="#9A32CD"
        android:textSize="15sp" />

</LinearLayout>
数据源类 Work

public class Work {

	private WorkGroup mWorkGroup;
	private List<WorkChild> mWorkChildList;

	public WorkGroup getmWorkGroup() {
		return mWorkGroup;
	}

	public void setmWorkGroup(WorkGroup mWorkGroup) {
		this.mWorkGroup = mWorkGroup;
	}

	public List<WorkChild> getmWorkChildList() {
		return mWorkChildList;
	}

	public void setmWorkChildList(List<WorkChild> mWorkChildList) {
		this.mWorkChildList = mWorkChildList;
	}

}
WorkGroup

public class WorkGroup {

	private int image1Group;// 图片
	private String titleGroup;// 表头名

	public int getImage1Group() {
		return image1Group;
	}

	public void setImage1Group(int image1Group) {
		this.image1Group = image1Group;
	}

	public String getTitleGroup() {
		return titleGroup;
	}

	public void setTitleGroup(String titleGroup) {
		this.titleGroup = titleGroup;
	}

}

WorkChild

public class WorkChild {

	private int imageChild;// 图片
	private String titleChild;// 表体名

	public int getImageChild() {
		return imageChild;
	}

	public void setImageChild(int imageChild) {
		this.imageChild = imageChild;
	}

	public String getTitleChild() {
		return titleChild;
	}

	public void setTitleChild(String titleChild) {
		this.titleChild = titleChild;
	}
}

下面是MyWorkAdapter,包括表头和表体View的填充
public class MyWorkAdapter extends BaseExpandableListAdapter {

	private List<Work> mList;
	private LayoutInflater mLayoutInflater;

	public MyWorkAdapter(Context context, List<Work> list) {

		mLayoutInflater = LayoutInflater.from(context);
		mList = list;
	}

	class GroupViewHolder {
		// Group内部类缓存
		ImageView groupImage;
		TextView groupTitle;
		ImageView groupIndicator;

	}

	class ChildViewHolder {
		// Child内部类缓存
		ImageView childImage;
		TextView childTitle;
	}

	@Override
	public int getGroupCount() {
		// 返回表头的数量,即List<Work>的长度
		return mList.size();
	}

	@Override
	public int getChildrenCount(int groupPosition) {
		// 返回当前组内有表体数量,即每一组中List<WorkChild>的长度
		return mList.get(groupPosition)// 得到当前的组Work
				.getmWorkChildList().size();// 得到List<WorkChild>的长度
	}

	@Override
	public Object getGroup(int groupPosition) {
		// 返回当前组的表头,即WorkGroup
		return mList.get(groupPosition)// 得到当前的组Work
				.getmWorkGroup();// 得到WorkGroup
	}

	@Override
	public Object getChild(int groupPosition, int childPosition) {
		// 返回当前组的当前表体项,即WorkChild
		return mList.get(groupPosition)// 得到当前的组Work
				.getmWorkChildList()// 得到List<WorkChild>
				.get(childPosition);// 得到WorkChild
	}

	@Override
	public long getGroupId(int groupPosition) {
		// 返回当前表头Id
		return groupPosition;
	}

	@Override
	public long getChildId(int groupPosition, int childPosition) {
		// 返回当前表体Id
		return childPosition;
	}

	@Override
	public View getGroupView(int groupPosition, boolean isExpanded,
			View convertView, ViewGroup parent) {
		// 返回表头View
		GroupViewHolder mGroupViewHolder = null;
		if (convertView == null) {
			mGroupViewHolder = new GroupViewHolder();
			// 内部类绑定相应控件
			convertView = mLayoutInflater.inflate(R.layout.work_groups, parent,
					false);
			mGroupViewHolder.groupImage = (ImageView) convertView
					.findViewById(R.id.group_image1);
			mGroupViewHolder.groupTitle = (TextView) convertView
					.findViewById(R.id.group_title);
			mGroupViewHolder.groupIndicator = (ImageView) convertView
					.findViewById(R.id.group_image2);
			// 内部类绑定当前载入的convertView
			convertView.setTag(mGroupViewHolder);
		} else {
			mGroupViewHolder = (GroupViewHolder) convertView.getTag();
		}
		// 当前convertView的数据源WorkGroup
		WorkGroup mWorkGroup = (WorkGroup) getGroup(groupPosition);
		// 设定控件资源
		mGroupViewHolder.groupImage.setBackgroundResource(mWorkGroup
				.getImage1Group());
		mGroupViewHolder.groupTitle.setText(mWorkGroup.getTitleGroup());
		// 表头的open和close设置不同image
		if (isExpanded) {
			mGroupViewHolder.groupIndicator
					.setBackgroundResource(R.drawable.work_open);
		} else {
			mGroupViewHolder.groupIndicator
					.setBackgroundResource(R.drawable.work_close);
		}
		return convertView;
	}

	@Override
	public View getChildView(int groupPosition, int childPosition,
			boolean isLastChild, View convertView, ViewGroup parent) {
		// 返回表体View,具体内容同getGroupView一样
		ChildViewHolder mChildViewHolder = null;
		if (convertView == null) {
			mChildViewHolder = new ChildViewHolder();
			// 内部类绑定相应控件
			convertView = mLayoutInflater.inflate(R.layout.work_childs, parent,
					false);
			mChildViewHolder.childImage = (ImageView) convertView
					.findViewById(R.id.child_image1);
			mChildViewHolder.childTitle = (TextView) convertView
					.findViewById(R.id.child_title);
			// 内部类绑定当前载入的convertView
			convertView.setTag(mChildViewHolder);
		} else {
			mChildViewHolder = (ChildViewHolder) convertView.getTag();
		}
		// 当前convertView的数据源WorkChild
		WorkChild mWorkChild = (WorkChild) getChild(groupPosition,
				childPosition);
		// 设定控件资源
		mChildViewHolder.childImage.setBackgroundResource(mWorkChild
				.getImageChild());
		mChildViewHolder.childTitle.setText(mWorkChild.getTitleChild());
		return convertView;
	}

	@Override
	public boolean hasStableIds() {
		// 是否指定分组视图及其子视图的Id对应的后台数据改变也会保持该Id
		return true;
	}

	@Override
	public boolean isChildSelectable(int groupPosition, int childPosition) {
		// 指定位置的子视图是否可选择
		return true;
	}

}
接下来在Activity中初始化二级列表

主布局 work_main

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#C9C9C9"
    android:orientation="vertical"
    android:paddingTop="20dp" >

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="10dp"
        android:text="技术"
        android:textColor="#EE0000"
        android:textSize="20sp" />

    <ExpandableListView
        android:id="@+id/list_1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>
ITWorkActivity

public class ITWorkActivity extends Activity {

	private ExpandableListView mExpandableListView;
	private MyWorkAdapter mMyWorkAdapter;
	// 数据源
	private List<Work> mWorkList;
	private Work mWork;
	private WorkGroup mWorkGroup;
	private List<WorkChild> mWorkChildList;
	private WorkChild mWorkChild;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		setContentView(R.layout.work_main);
		// 初始化数据
		init();
		mExpandableListView = (ExpandableListView) findViewById(R.id.list_1);
		// 去除默认的指示器
		mExpandableListView.setGroupIndicator(null);
		mMyWorkAdapter = new MyWorkAdapter(getApplicationContext(), mWorkList);
		mExpandableListView.setAdapter(mMyWorkAdapter);
	}

	private void init() {
		mWorkList = new ArrayList<Work>();
		// 后端开发
		mWork = new Work();
		// 表头
		mWorkGroup = new WorkGroup();
		mWorkGroup.setImage1Group(R.drawable.life_logo);
		mWorkGroup.setTitleGroup("后端开发");
		mWork.setmWorkGroup(mWorkGroup);
		// 表体
		mWorkChildList = new ArrayList<WorkChild>();
		mWorkChild = new WorkChild();// Java
		mWorkChild.setImageChild(R.drawable.life_logo);
		mWorkChild.setTitleChild("Java");
		mWorkChildList.add(mWorkChild);
		mWorkChild = new WorkChild();// PHP
		mWorkChild.setImageChild(R.drawable.life_logo);
		mWorkChild.setTitleChild("PHP");
		mWorkChildList.add(mWorkChild);
		mWorkChild = new WorkChild();// Python
		mWorkChild.setImageChild(R.drawable.life_logo);
		mWorkChild.setTitleChild("Python");
		mWorkChildList.add(mWorkChild);
		mWorkChild = new WorkChild();// C#
		mWorkChild.setImageChild(R.drawable.life_logo);
		mWorkChild.setTitleChild("C#");
		mWorkChildList.add(mWorkChild);
		mWorkChild = new WorkChild();// C++
		mWorkChild.setImageChild(R.drawable.life_logo);
		mWorkChild.setTitleChild("C++");
		mWorkChildList.add(mWorkChild);
		mWorkChild = new WorkChild();// C
		mWorkChild.setImageChild(R.drawable.life_logo);
		mWorkChild.setTitleChild("C");
		mWorkChildList.add(mWorkChild);
		mWorkChild = new WorkChild();// 后端开发其他
		mWorkChild.setImageChild(R.drawable.life_logo);
		mWorkChild.setTitleChild("后端开发其他");
		mWorkChildList.add(mWorkChild);
		mWork.setmWorkChildList(mWorkChildList);
		mWorkList.add(mWork);
		// 移动开发
		mWork = new Work();
		mWorkGroup = new WorkGroup();
		mWorkGroup.setImage1Group(R.drawable.life_logo);
		mWorkGroup.setTitleGroup("移动开发");
		mWork.setmWorkGroup(mWorkGroup);
		mWorkChildList = new ArrayList<WorkChild>();
		mWorkChild = new WorkChild();// IOS
		mWorkChild.setImageChild(R.drawable.life_logo);
		mWorkChild.setTitleChild("IOS");
		mWorkChildList.add(mWorkChild);
		mWorkChild = new WorkChild();// Android
		mWorkChild.setImageChild(R.drawable.life_logo);
		mWorkChild.setTitleChild("Android");
		mWorkChildList.add(mWorkChild);
		mWorkChild = new WorkChild();// WP
		mWorkChild.setImageChild(R.drawable.life_logo);
		mWorkChild.setTitleChild("WP");
		mWorkChildList.add(mWorkChild);
		mWorkChild = new WorkChild();// 移动开发其他
		mWorkChild.setImageChild(R.drawable.life_logo);
		mWorkChild.setTitleChild("移动开发其他");
		mWorkChildList.add(mWorkChild);
		mWork.setmWorkChildList(mWorkChildList);
		mWorkList.add(mWork);

	}

}
最终成品图如下:


到此一个完全自定义的静态二级列表就完全OK了,但仅仅是展示内容是远远不够的,在BaseExpandableListAdapter,二级列表的完全自定义(二)

中会实现二级列表的各种手势和点击事件,默认展开项,速滑事件,表体点击事件,表头和表体的长按事件,比如删除分组名,添加分组等等。

好了,这章到此结束,有什么错误或能优化的地方恳请多提意见和建议。








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值