Android group listview

来源:互联网 发布:mac如何打包文件 编辑:程序博客网 时间:2024/06/08 03:30

这几天做Android开发,发现android中缺少一个Group ListView的实现

如果仅仅通过Adapter来实现的话,分组和组内位置就需要自己实现,所以就参考android中的ExpandableListView做了一个GroupListView,用来快速放置分组列表.

实现了组标签的置顶效果,同时修正了在android2.1中出现的组标签置顶出现的问题


废话不多说,贴出核心的源代码以供参考

EarlGroupListView.java

package com.list;import android.content.Context;import android.graphics.Canvas;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import android.widget.ListAdapter;import android.widget.ListView;import com.list.EarlGroupConnector.GroupMetadata;import com.list.EarlGroupConnector.PositionMetadata;public class EarlGroupListView extends ListView {/** 浮动的组View */private View mfloatingView;/** 是否显示浮动view */private boolean mIsHeaderShow;/** 浮动view被裁剪的高度 */private int mClipHeaderHeight;private EarlGroupConnector mConnector;public EarlGroupListView(Context context) {super(context);init();}public EarlGroupListView(Context context, AttributeSet attrs) {super(context, attrs);init();}public EarlGroupListView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init();}private void init() {setFadingEdgeLength(0);}@Overrideprotected void dispatchDraw(Canvas canvas) {super.dispatchDraw(canvas);// 可视区域中的view的个数int childCount = getChildCount();// 获取第一个可视的view的位置,绝对位置int first = getFirstVisiblePosition();// paddingTopint paddingTop = getPaddingTop();View firstVisibleView = null;// 由于在android2.1中出现不准确的情况,我们修正它// 当然在android2.2及以后的版本中也可以使用,循环将在一次循环后结束int offset = 0;for (int i = 0; i < childCount; i++) {View v = getChildAt(i);// 找到第一个显示的view了if (v.getBottom() > paddingTop) {offset = i;firstVisibleView = v;break;}// first继续向后查找first++;}// Log.i("first" + (getCount() - getFooterViewsCount()), "" + first);// 这里的first位置是list中的绝对位置// 所以需要线判断这个位置是否是header或者footerfinal int headerViewsCount = getHeaderViewsCount();final int footerViewsStart = getCount() - getFooterViewsCount();if (first < headerViewsCount || first >= footerViewsStart) {mIsHeaderShow = false;return;}// 然后将绝对位置转换为expandableListView可以使用的flat位置first -= headerViewsCount;PositionMetadata pm = mConnector.getUnflattenedPos(first);int type = pm.type;int groupPos = pm.groupPos;pm.recycle();// 第一个看到的是分组,在最上面并且它顶在最上方if (type == PositionMetadata.TYPE_GROUP&& firstVisibleView.getTop() == paddingTop) {mIsHeaderShow = false;return;}// 获取更新的浮动viewView floatingView = mConnector.getAdapter().getGroupView(groupPos,mfloatingView, this);// 当保存的view和新的view不同时,重新布局及调整大小if (floatingView != mfloatingView) {mfloatingView = floatingView;android.widget.AbsListView.LayoutParams layoutparams = (android.widget.AbsListView.LayoutParams) floatingView.getLayoutParams();// 布局参数if (layoutparams == null) {layoutparams = new android.widget.AbsListView.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT, 0);floatingView.setLayoutParams(layoutparams);}// 大小和布局,大小同第一个分组int widthMeasureSpec = getChildMeasureSpec(0, 0,firstVisibleView.getWidth());int heightMeasureSpec = getChildMeasureSpec(0, 0,firstVisibleView.getHeight());floatingView.measure(widthMeasureSpec, heightMeasureSpec);floatingView.layout(getPaddingLeft(), paddingTop,firstVisibleView.getRight(), firstVisibleView.getHeight()+ paddingTop);floatingView.setClickable(true);}int nextGroupPos = -1;// 第一个可视分组不是最后一个分组if (groupPos != mConnector.getAdapter().getGroupCount() - 1) {nextGroupPos = groupPos + 1;GroupMetadata nextPm = mConnector.getGroupMetadatas().get(nextGroupPos);int nextPos = nextPm.mGroupFlatPos;View nextGroupView = getChildAt(nextPos - first + offset);// 下一个分组组可以被索引到,它不在屏幕外if (nextGroupView != null) {int top = nextGroupView.getTop() - getDividerHeight();mClipHeaderHeight = mfloatingView.getBottom() > top ? mfloatingView.getBottom() - top: 0;} else {mClipHeaderHeight = 0;}} else {mClipHeaderHeight = 0;}mIsHeaderShow = true;canvas.save();// 裁剪掉超出padding的部分if (mClipHeaderHeight > 0)canvas.clipRect(0, paddingTop, getWidth(),mfloatingView.getBottom());canvas.translate(0, -mClipHeaderHeight);drawChild(canvas, mfloatingView, getDrawingTime());canvas.restore();}@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {float y = ev.getY();if (mIsHeaderShow&& y < (mfloatingView.getHeight() - mClipHeaderHeight)) {switch (ev.getAction()) {case MotionEvent.ACTION_UP:if (mfloatingView.isPressed()) {mfloatingView.setPressed(false);// TODO add click header event}break;}return true;} else {if (mfloatingView != null && mfloatingView.isPressed()) {mfloatingView.setPressed(false);invalidate();}return super.dispatchTouchEvent(ev);}}@Overridepublic void setAdapter(ListAdapter adapter) {throw new IllegalArgumentException("you should use setAdapter(EarlGroupListAdapter adapter),not this");}public void setAdapter(EarlGroupListAdapter adapter) {mConnector = new EarlGroupConnector(adapter);super.setAdapter(mConnector);}}


EarlGroupConnector.java,主要用于处理分组问题

package com.list;import java.util.ArrayList;import android.database.DataSetObserver;import android.os.Parcel;import android.os.Parcelable;import android.util.Log;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;public class EarlGroupConnector extends BaseAdapter {private ArrayList<GroupMetadata> mGroupMetadatas;private EarlGroupListAdapter mEarlGroupListAdapter;private final MyDataSetObserver myDataSetObserver = new MyDataSetObserver();private int mTotalCount;EarlGroupConnector(EarlGroupListAdapter adapter) {mGroupMetadatas = new ArrayList<EarlGroupConnector.GroupMetadata>();setEarlGroupListAdapter(adapter);}public int getCount() {return mTotalCount;}public Object getItem(int flatListPos) {final PositionMetadata posMetadata = getUnflattenedPos(flatListPos);Object retValue;if (posMetadata.type == PositionMetadata.TYPE_GROUP) {retValue = mEarlGroupListAdapter.getGroup(posMetadata.groupPos);} else if (posMetadata.type == PositionMetadata.TYPE_CHILD) {retValue = mEarlGroupListAdapter.getChild(posMetadata.groupPos,posMetadata.childPos);} else {// TODO: clean exitthrow new RuntimeException("Flat list position is of unknown type");}posMetadata.recycle();return retValue;}public long getItemId(int flatListPos) {final PositionMetadata posMetadata = getUnflattenedPos(flatListPos);final long groupId = mEarlGroupListAdapter.getGroupId(posMetadata.groupPos);long retValue;if (posMetadata.type == PositionMetadata.TYPE_GROUP) {retValue = mEarlGroupListAdapter.getCombinedGroupId(groupId);} else if (posMetadata.type == PositionMetadata.TYPE_CHILD) {final long childId = mEarlGroupListAdapter.getChildId(posMetadata.groupPos, posMetadata.childPos);retValue = mEarlGroupListAdapter.getCombinedChildId(groupId,childId);} else {// TODO: clean exitthrow new RuntimeException("Flat list position is of unknown type");}posMetadata.recycle();return retValue;}public View getView(int flatListPos, View convertView, ViewGroup parent) {final PositionMetadata posMetadata = getUnflattenedPos(flatListPos);View retValue;if (posMetadata.type == PositionMetadata.TYPE_GROUP) {retValue = mEarlGroupListAdapter.getGroupView(posMetadata.groupPos,convertView, parent);} else if (posMetadata.type == PositionMetadata.TYPE_CHILD) {retValue = mEarlGroupListAdapter.getChildView(posMetadata.groupPos,posMetadata.childPos, convertView, parent);} else {// TODO: clean exitthrow new RuntimeException("Flat list position is of unknown type");}posMetadata.recycle();return retValue;}@Overridepublic int getViewTypeCount() {return 2;}@Overridepublic int getItemViewType(int flatListPos) {final PositionMetadata pos = getUnflattenedPos(flatListPos);int retValue;if (pos.type == PositionMetadata.TYPE_GROUP) {retValue = 0;} else {retValue = 1;}pos.recycle();return retValue;}@Overridepublic boolean isEmpty() {return mEarlGroupListAdapter == null || mEarlGroupListAdapter.isEmpty();}@Overridepublic boolean areAllItemsEnabled() {return mEarlGroupListAdapter.areAllItemsEnabled();}@Overridepublic boolean isEnabled(int flatListPos) {final PositionMetadata pos = getUnflattenedPos(flatListPos);boolean retValue = pos.type == PositionMetadata.TYPE_CHILD;pos.recycle();return retValue;};PositionMetadata getUnflattenedPos(int flatListPos) {int type = 0;int groupPos = -1;int childPos = -1;GroupMetadata groupMetadata = null;int start = 0;int end = mGroupMetadatas.size() - 1;int mid = 0;while (start <= end) {mid = (start + end) / 2;GroupMetadata midMetadata = mGroupMetadatas.get(mid);/** * the current item is group item, we stop search */if (flatListPos == midMetadata.mGroupFlatPos) {type = PositionMetadata.TYPE_GROUP;groupPos = mid;groupMetadata = midMetadata;break;}/** * the current flatPos item is in the back of middle group */else if (flatListPos > midMetadata.mGroupFlatPos+ midMetadata.mChildCount) {start = mid + 1;}/** * the current flatPos item is in the fromt of middle group */else if (flatListPos < midMetadata.mGroupFlatPos) {end = mid - 1;}/** * the current item is a child */else if (flatListPos <= midMetadata.mGroupFlatPos+ midMetadata.mChildCount/* &&flatListPos<midMetadata.mGroupFlatPos */) {type = PositionMetadata.TYPE_CHILD;groupPos = mid;childPos = flatListPos - midMetadata.mGroupFlatPos - 1;groupMetadata = midMetadata;break;}}return PositionMetadata.obtain(flatListPos, type, groupPos, childPos,groupMetadata);}public void setEarlGroupListAdapter(EarlGroupListAdapter adapter) {if (mEarlGroupListAdapter != null) {mEarlGroupListAdapter.unregisterDataSetObserver(myDataSetObserver);}mEarlGroupListAdapter = adapter;adapter.registerDataSetObserver(myDataSetObserver);buildGroupData();}public EarlGroupListAdapter getAdapter() {return mEarlGroupListAdapter;}void buildGroupData() {// TODO reuse the arrayList dataif (mEarlGroupListAdapter != null) {int count = mEarlGroupListAdapter.getGroupCount();int flatPos = 0;for (int i = 0; i < count; i++) {GroupMetadata metadata = new GroupMetadata();metadata.mGroupPos = i;metadata.mChildCount = mEarlGroupListAdapter.getChildrenCount(i);metadata.mGroupFlatPos = flatPos;flatPos += (metadata.mChildCount + 1);mGroupMetadatas.add(metadata);}mTotalCount = flatPos;}}public ArrayList<GroupMetadata> getGroupMetadatas() {return mGroupMetadatas;}public void setGroupMetadata(ArrayList<GroupMetadata> groupMetadatas) {if (groupMetadatas == null || mEarlGroupListAdapter == null)return;mGroupMetadatas = groupMetadatas;}/** * the metadata of EarlGroupListView to hold the group information *  * @author ashyearl *  */static class GroupMetadata implements Parcelable {/** this group's group position */int mGroupPos;/** This group's flat list position */int mGroupFlatPos;/** this group's child count */int mChildCount;public static GroupMetadata obtain(int groupPos, int groupFlatPos,int childCount) {GroupMetadata metadata = new GroupMetadata();metadata.mGroupPos = groupPos;metadata.mGroupFlatPos = groupFlatPos;metadata.mChildCount = childCount;return metadata;}public int describeContents() {return 0;}/** * save single GroupMetadata to Parcel */public void writeToParcel(Parcel dest, int flags) {Log.i("writeToParcel", "" + dest);dest.writeInt(mGroupPos);dest.writeInt(mGroupFlatPos);dest.writeInt(mChildCount);}public static final Parcelable.Creator<GroupMetadata> CREATOR = new Parcelable.Creator<GroupMetadata>() {public GroupMetadata createFromParcel(Parcel in) {GroupMetadata gm = GroupMetadata.obtain(in.readInt(),in.readInt(), in.readInt());return gm;}public GroupMetadata[] newArray(int size) {return new GroupMetadata[size];}};}protected class MyDataSetObserver extends DataSetObserver {@Overridepublic void onChanged() {buildGroupData();notifyDataSetChanged();}@Overridepublic void onInvalidated() {buildGroupData();notifyDataSetInvalidated();}}/** * Data type that contains an group list position (can refer to either a * group or child) */static public class PositionMetadata {private static final int MAX_POOL_SIZE = 5;public static final int TYPE_CHILD = 1;public static final int TYPE_GROUP = 2;private static ArrayList<PositionMetadata> sPool = new ArrayList<PositionMetadata>(MAX_POOL_SIZE);/** the flat position */public int flatPosition;/** the type of current flat position */public int type;public int groupPos;public int childPos;/** * Link back to the group GroupMetadata for this group. Useful for * removing the group from the list of expanded groups inside the * connector when we collapse the group, and also as a check to see if * the group was expanded or collapsed (this will be null if the group * is collapsed since we don't keep that group's metadata) */public GroupMetadata groupMetadata;private void resetState() {flatPosition = 0;groupMetadata = null;type = TYPE_CHILD;}/** * Use {@link #obtain(int, int, int, int, GroupMetadata, int)} */private PositionMetadata() {}static PositionMetadata obtain(int flatListPos, int type, int groupPos,int childPos, GroupMetadata groupMetadata) {PositionMetadata pm = getRecycledOrCreate();pm.flatPosition = flatListPos;pm.type = type;pm.groupPos = groupPos;pm.childPos = childPos;pm.groupMetadata = groupMetadata;return pm;}private static PositionMetadata getRecycledOrCreate() {PositionMetadata pm;synchronized (sPool) {if (sPool.size() > 0) {pm = sPool.remove(0);} else {return new PositionMetadata();}}pm.resetState();return pm;}public void recycle() {synchronized (sPool) {if (sPool.size() < MAX_POOL_SIZE) {sPool.add(this);}}}}}
EarlGroupListAdapter.java,适配器

package com.list;import android.database.DataSetObservable;import android.database.DataSetObserver;import android.view.View;import android.view.ViewGroup;import android.widget.Adapter;import android.widget.ListAdapter;public abstract class EarlGroupListAdapter {    private final DataSetObservable mDataSetObservable = new DataSetObservable();        public void registerDataSetObserver(DataSetObserver observer) {        mDataSetObservable.registerObserver(observer);    }    public void unregisterDataSetObserver(DataSetObserver observer) {        mDataSetObservable.unregisterObserver(observer);    }    /**     * @see DataSetObservable#notifyInvalidated()     */    public void notifyDataSetInvalidated() {        mDataSetObservable.notifyInvalidated();    }        /**     * @see DataSetObservable#notifyChanged()     */    public void notifyDataSetChanged() {        mDataSetObservable.notifyChanged();    }/** * Gets the number of groups. *  * @return the number of groups */abstract int getGroupCount();/** * Gets the number of children in a specified group. *  * @param groupPosition *            the position of the group for which the children count should *            be returned * @return the children count in the specified group */abstract int getChildrenCount(int groupPosition);/** * Gets the data associated with the given group. *  * @param groupPosition *            the position of the group * @return the data child for the specified group */abstract Object getGroup(int groupPosition);/** * Gets the data associated with the given child within the given group. *  * @param groupPosition *            the position of the group that the child resides in * @param childPosition *            the position of the child with respect to other children in *            the group * @return the data of the child */abstract Object getChild(int groupPosition, int childPosition);/** * Gets the ID for the group at the given position. This group ID must be * unique across groups. The combined ID (see * {@link #getCombinedGroupId(long)}) must be unique across ALL items * (groups and all children). *  * @param groupPosition *            the position of the group for which the ID is wanted * @return the ID associated with the group */abstract long getGroupId(int groupPosition);/** * Gets the ID for the given child within the given group. This ID must be * unique across all children within the group. The combined ID (see * {@link #getCombinedChildId(long, long)}) must be unique across ALL items * (groups and all children). *  * @param groupPosition *            the position of the group that contains the child * @param childPosition *            the position of the child within the group for which the ID is *            wanted * @return the ID associated with the child */abstract long getChildId(int groupPosition, int childPosition);/** * Indicates whether the child and group IDs are stable across changes to * the underlying data. *  * @return whether or not the same ID always refers to the same object * @see Adapter#hasStableIds() */abstract boolean hasStableIds();/** * Gets a View that displays the given group. This View is only for the * group--the Views for the group's children will be fetched using * {@link #getChildView(int, int, boolean, View, ViewGroup)}. *  * @param groupPosition *            the position of the group for which the View is returned * @param convertView *            the old view to reuse, if possible. You should check that this *            view is non-null and of an appropriate type before using. If *            it is not possible to convert this view to display the correct *            data, this method can create a new view. It is not guaranteed *            that the convertView will have been previously created by *            {@link #getGroupView(int, boolean, View, ViewGroup)}. * @param parent *            the parent that this view will eventually be attached to * @return the View corresponding to the group at the specified position */abstract View getGroupView(int groupPosition, View convertView, ViewGroup parent);/** * Gets a View that displays the data for the given child within the given * group. *  * @param groupPosition *            the position of the group that contains the child * @param childPosition *            the position of the child (for which the View is returned) *            within the group * @param convertView *            the old view to reuse, if possible. You should check that this *            view is non-null and of an appropriate type before using. If *            it is not possible to convert this view to display the correct *            data, this method can create a new view. It is not guaranteed *            that the convertView will have been previously created by *            {@link #getChildView(int, int, boolean, View, ViewGroup)}. * @param parent *            the parent that this view will eventually be attached to * @return the View corresponding to the child at the specified position */abstract View getChildView(int groupPosition, int childPosition, View convertView,ViewGroup parent);/** * Whether the child at the specified position is selectable. *  * @param groupPosition *            the position of the group that contains the child * @param childPosition *            the position of the child within the group * @return whether the child is selectable. */abstract boolean isChildSelectable(int groupPosition, int childPosition);/** * @see ListAdapter#areAllItemsEnabled() */boolean areAllItemsEnabled(){return false;}/** * @see ListAdapter#isEmpty() */abstract boolean isEmpty();    /**     * Override this method if you foresee a clash in IDs based on this scheme:     * <p>     * Base implementation returns a long:     * <li> bit 0: Whether this ID points to a child (unset) or group (set), so for this method     *             this bit will be 1.     * <li> bit 1-31: Lower 31 bits of the groupId     * <li> bit 32-63: Lower 32 bits of the childId.     * <p>      * {@inheritDoc}     */    public long getCombinedChildId(long groupId, long childId) {        return 0x8000000000000000L | ((groupId & 0x7FFFFFFF) << 32) | (childId & 0xFFFFFFFF);    }    /**     * Override this method if you foresee a clash in IDs based on this scheme:     * <p>     * Base implementation returns a long:     * <li> bit 0: Whether this ID points to a child (unset) or group (set), so for this method     *             this bit will be 0.     * <li> bit 1-31: Lower 31 bits of the groupId     * <li> bit 32-63: Lower 32 bits of the childId.     * <p>      * {@inheritDoc}     */    public long getCombinedGroupId(long groupId) {        return (groupId & 0x7FFFFFFF) << 32;    }}

EarlSimpleGroupListAdapter.java,一个简单的适配器,类似SimpleListAdapter

package com.list;import java.util.List;import java.util.Map;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.ExpandableListView;import android.widget.TextView;/** * An easy adapter to map static data to group and child views defined in an XML * file. You can separately specify the data backing the group as a List of * Maps. Each entry in the ArrayList corresponds to one group in the expandable * list. The Maps contain the data for each row. You also specify an XML file * that defines the views used to display a group, and a mapping from keys in * the Map to specific views. This process is similar for a child, except it is * one-level deeper so the data backing is specified as a List<List<Map>>, where * the first List corresponds to the group of the child, the second List * corresponds to the position of the child within the group, and finally the * Map holds the data for that particular child. */public class EarlSimpleGroupListAdapter extends EarlGroupListAdapter {private List<? extends Map<String, ?>> mGroupData;private int mGroupLayout;private String[] mGroupFrom;private int[] mGroupTo;private List<? extends List<? extends Map<String, ?>>> mChildData;private int mChildLayout;private String[] mChildFrom;private int[] mChildTo;private LayoutInflater mInflater;/** * Constructor *  * @param context *            The context where the {@link ExpandableListView} associated *            with this {@link EarlSimpleGroupListAdapter} is running * @param groupData *            A List of Maps. Each entry in the List corresponds to one *            group in the list. The Maps contain the data for each group, *            and should include all the entries specified in "groupFrom" * @param groupFrom *            A list of keys that will be fetched from the Map associated *            with each group. * @param groupTo *            The group views that should display column in the "groupFrom" *            parameter. These should all be TextViews. The first N views in *            this list are given the values of the first N columns in the *            groupFrom parameter. * @param groupLayout *            resource identifier of a view layout that defines the views *            for a group. The layout file should include at least those *            named views defined in "groupTo" * @param childData *            A List of List of Maps. Each entry in the outer List *            corresponds to a group (index by group position), each entry *            in the inner List corresponds to a child within the group *            (index by child position), and the Map corresponds to the data *            for a child (index by values in the childFrom array). The Map *            contains the data for each child, and should include all the *            entries specified in "childFrom" * @param childFrom *            A list of keys that will be fetched from the Map associated *            with each child. * @param childTo *            The child views that should display column in the "childFrom" *            parameter. These should all be TextViews. The first N views in *            this list are given the values of the first N columns in the *            childFrom parameter. * @param childLayout *            resource identifier of a view layout that defines the views *            for a child. The layout file should include at least those *            named views defined in "childTo" */public EarlSimpleGroupListAdapter(Context context,List<? extends Map<String, ?>> groupData, int groupLayout,String[] groupFrom, int[] groupTo,List<? extends List<? extends Map<String, ?>>> childData,int childLayout, String[] childFrom, int[] childTo) {mGroupData = groupData;mGroupLayout = groupLayout;mGroupFrom = groupFrom;mGroupTo = groupTo;mChildData = childData;mChildLayout = childLayout;mChildFrom = childFrom;mChildTo = childTo;mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);}public Object getChild(int groupPosition, int childPosition) {return mChildData.get(groupPosition).get(childPosition);}public long getChildId(int groupPosition, int childPosition) {return childPosition;}public View getChildView(int groupPosition, int childPosition,View convertView, ViewGroup parent) {View v;if (convertView == null) {v = mInflater.inflate(mChildLayout, parent, false);} else {v = convertView;}bindView(v, mChildData.get(groupPosition).get(childPosition),mChildFrom, mChildTo);return v;}private void bindView(View view, Map<String, ?> data, String[] from,int[] to) {int len = to.length;for (int i = 0; i < len; i++) {TextView v = (TextView) view.findViewById(to[i]);if (v != null) {v.setText((String) data.get(from[i]));}}}public int getChildrenCount(int groupPosition) {return mChildData.get(groupPosition).size();}public Object getGroup(int groupPosition) {return mGroupData.get(groupPosition);}public int getGroupCount() {return mGroupData.size();}public long getGroupId(int groupPosition) {return groupPosition;}public View getGroupView(int groupPosition, View convertView,ViewGroup parent) {View v;if (convertView == null) {v = mInflater.inflate(mGroupLayout, parent, false);} else {v = convertView;}bindView(v, mGroupData.get(groupPosition), mGroupFrom, mGroupTo);return v;}public boolean isChildSelectable(int groupPosition, int childPosition) {return true;}public boolean hasStableIds() {return true;}@Overrideboolean isEmpty() {return getGroupCount() == 0;}}


测试用的Activity,其他的布局文件就没啥了

package com.list;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;import android.app.Activity;import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.AdapterView;import android.widget.AdapterView.OnItemClickListener;import android.widget.TextView;public class ListActivity extends Activity {String A = "A";String B = "B";@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);EarlGroupListView expandableListView = (EarlGroupListView) findViewById(R.id.expandableListView1);List<Map<String, String>> groupData = new ArrayList<Map<String, String>>();List<List<Map<String, String>>> childData = new ArrayList<List<Map<String, String>>>();for (int i = 0; i < 10; i++) {Map<String, String> curGroupMap = new HashMap<String, String>();groupData.add(curGroupMap);curGroupMap.put(A, "Group " + i);curGroupMap.put(B, (i % 2 == 0) ? "This group is even": "This group is odd");List<Map<String, String>> children = new ArrayList<Map<String, String>>();for (int j = 0; j < 3; j++) {Map<String, String> curChildMap = new HashMap<String, String>();children.add(curChildMap);curChildMap.put(A, "Child " + j);curChildMap.put(B, (j % 2 == 0) ? "This child is even": "This child is odd");}childData.add(children);}for (int i = 0; i < 7; i++) {TextView textView = new TextView(this);textView.setText("我是一个测试" + i);expandableListView.addHeaderView(textView);}for (int i = 0; i < 26; i++) {TextView textView = new TextView(this);textView.setText("我是footer测试" + i);expandableListView.addFooterView(textView);}expandableListView.addHeaderView(getLayoutInflater().inflate(R.layout.header, null));expandableListView.setAdapter(new EarlSimpleGroupListAdapter(this,groupData, R.layout.group, new String[] { A, B }, new int[] {R.id.textView1, R.id.textView2 }, childData,R.layout.item, new String[] { A, B }, new int[] {R.id.textView1, R.id.textView2 }));expandableListView.setOnItemClickListener(new OnItemClickListener() {public void onItemClick(AdapterView<?> parent, View view,int position, long id) {Log.i("onItemClick", "" + position);}});}}

贴上在布局文件中的使用

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="fill_parent"    android:layout_height="fill_parent"    android:padding="10dp"    android:orientation="vertical" >    <Button        android:id="@+id/button2"        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:text="Button" />    <com.list.EarlGroupListView        android:id="@+id/expandableListView1"        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:layout_weight="1"        android:background="@drawable/list_single_normal"        android:cacheColorHint="#00000000" >    </com.list.EarlGroupListView>    <Button        android:id="@+id/button1"        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:text="Button" /></LinearLayout>