1. 程式人生 > >BaseExpandableListAdapter,二級列表的完全自定義(一)

BaseExpandableListAdapter,二級列表的完全自定義(一)

普通的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,二級列表的完全自定義(二)

中會實現二級列表的各種手勢和點選事件,預設展開項,速滑事件,表體點選事件,表頭和表體的長按事件,比如刪除分組名,新增分組等等。

好了,這章到此結束,有什麼錯誤或能優化的地方懇請多提意見和建議。