普通的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,二级列表的完全自定义(二)
中会实现二级列表的各种手势和点击事件,默认展开项,速滑事件,表体点击事件,表头和表体的长按事件,比如删除分组名,添加分组等等。
好了,这章到此结束,有什么错误或能优化的地方恳请多提意见和建议。