expand

[Android]界面的布局-ExpandableListView

http://ysl-paradise.blogspot.com/2011/05/listview-ii.html

1.可展开的ListView

在這一篇中,我們來看看如何自定义一个两层结构的ListView。如下图所示:

[Android]界面的布局-ExpandableListView

当点击PeopleNames,就显示对应的子列表,如下图:

[Android]界面的布局-ExpandableListView

如何可以做到上面的效果呢?首先我们自定义一个MyExpandableListAdapter.它继承自BaseExpandableListAdpter.

publicclassMyExpandableListAdapterextendsBaseExpandableListAdapter{

//Sampledataset.children[i]containsthechildren(String[])forgroups[i].

privateString[]groups={"PeopleNames","DogNames","CatNames","FishNames"};

privateString[][]children={

{"Arnold","Barry","Chuck","David"},

{"Ace","Bandit","Cha-Cha","Deuce"},

{"Fluffy","Snuggles"},

{"Goldy","Bubbles"}

};

publicObjectgetChild(intgroupPosition,intchildPosition){

returnchildren[groupPosition][childPosition];

}

publiclonggetChildId(intgroupPosition,intchildPosition){

returnchildPosition;

}

publicintgetChildrenCount(intgroupPosition){

returnchildren[groupPosition].length;

}

publicTextViewgetGenericView(){

//LayoutparametersfortheExpandableListView

AbsListView.LayoutParamslp=newAbsListView.LayoutParams(

ViewGroup.LayoutParams.MATCH_PARENT,64);

TextViewtextView=newTextView(TestExpandableListActivity.this);

textView.setLayoutParams(lp);

//Centerthetextvertically

textView.setGravity(Gravity.CENTER_VERTICAL|Gravity.LEFT);

//Setthetextstartingposition

textView.setPadding(60,0,0,0);//第一层的textview,距离上下左右边界的距离。

returntextView;

}

publicViewgetChildView(intgroupPosition,intchildPosition,booleanisLastChild,

ViewconvertView,ViewGroupparent){

TextViewtextView=getGenericView();

textView.setText(getChild(groupPosition,childPosition).toString());

returntextView;

}

publicObjectgetGroup(intgroupPosition){

returngroups[groupPosition];

}

publicintgetGroupCount(){

returngroups.length;

}

publiclonggetGroupId(intgroupPosition){

returngroupPosition;

}

publicViewgetGroupView(intgroupPosition,booleanisExpanded,ViewconvertView,

ViewGroupparent){

TextViewtextView=getGenericView();

textView.setText(getGroup(groupPosition).toString());

returntextView;

}

publicbooleanisChildSelectable(intgroupPosition,intchildPosition){

Toast.makeText(TestExpandableListActivity.this,getChild(groupPosition,childPosition).toString(),Toast.LENGTH_LONG).show();

returntrue;

}

publicbooleanhasStableIds(){

returntrue;

}

}

接着,新建一个工程,在主Activity里加入下面代码:

publicclassTestExpandableListActivityextendsExpandableListActivity{

ExpandableListAdaptermAdapter;

@Override

publicvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

//setContentView(R.layout.main);

//setupouradapter

mAdapter=newMyExpandableListAdapter();

setListAdapter(mAdapter);

registerForContextMenu(getExpandableListView());

}

}

上面的源代码在你本机c:\ProgramFiles\Android\android-sdk\samples\android-10_1\ApiDemos\src\com\example\android\apis\view\ExpandableList1.java

里可以找到。

2.自定义ExpandableListView的显示

上面的可展开的ListView黑乎乎的,且间隔很大。如何修改它的Layout呢?

2.1修改父界面。效果如下图:

[Android]界面的布局-ExpandableListView

新建一个xml文件,用来显示父界面。xml内容如下:

<?xmlversion="1.0"encoding="utf-8"?>

<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="fill_parent"android:orientation="horizontal"

android:layout_height="wrap_content"android:background="#ccc"

android:gravity="left|center_vertical"android:minheight="40dip"

android:paddingRight="20dip"android:paddingLeft="10dip">

<TextViewandroid:id="@+id/txtGroupName"

android:layout_height="wrap_content"

android:layout_width="0dip"

android:layout_weight="1"

android:textSize="20dip"

android:textColor="#000"

android:paddingLeft="3dip"

android:singleLine="true"/>

</LinearLayout>

其实只有一个textview控件,用来显示A,B,C,D。。。左边的展开折叠按钮时系统提供的。现在就先不修改它了。

用这个xml文件,主要是想设置奇偶行的颜色,这样使用起来比较清楚。不然背景是黑乎乎一片,使用起来不是那么舒服。

源代码里只需要修改一个函数。如下:

publicViewgetGroupView(intgroupPosition,booleanisExpanded,

ViewconvertView,ViewGroupparent){

StringstrGroup=mCategoryList.get(groupPosition);//自己取得要显示内容。mCategoryList其实就是一个List<String>里面存放了26个字母而已。

//设置Layout

LayoutInflaterinflater=(LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

LinearLayoutll=(LinearLayout)inflater.inflate(R.layout.artistlist_group,null);

TextViewtv=(TextView)ll.findViewById(R.id.txtGroupName);

tv.setGravity(Gravity.CENTER_VERTICAL|Gravity.LEFT);

//设置字符开始的位置,如果不够大的话,字符就和左边的展开/折叠图标重叠了。自己可以改改数字,看看效果就明白了。

tv.setPadding(60,0,0,0);

//设置显示的内容

tv.setText(strGroup);

//设置奇偶行的背景交错

ll.setBackgroundColor(groupPosition%2==0?Color.argb(250,255,255,255):Color.argb(250,229,229,229));

returnll;

}

2.2修改展开后的子界面。效果如下图:

[Android]界面的布局-ExpandableListView

其实和上面未展开前的父界面差不多,是不是?

新建一个xml文件,用来显示子界面。

<?xmlversion="1.0"encoding="utf-8"?>

<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="fill_parent"android:orientation="horizontal"

android:layout_height="wrap_content"android:background="#fff"

android:gravity="left|center_vertical"android:minheight="40dip"

android:paddingRight="20dip"android:paddingLeft="10dip">

<TextViewandroid:id="@+id/txtArtistName"

android:layout_height="wrap_content"

android:layout_width="0dip"

android:layout_weight="1"

android:textSize="16dip"

android:textColor="#000"

android:paddingLeft="3dip"

android:singleLine="true"/>

<ImageViewandroid:id="@+id/imgArtist"

android:src="@drawable/arrow_right_rest"

android:paddingRight="5dip"

android:layout_width="24dip"

android:layout_height="24dip"

android:contentDescription="@string/artist"/>

</LinearLayout>

从上面可以看出,textview用来显示文字,图片就是右边的那个箭头符号。

源代码如下:

publicViewgetChildView(intgroupPosition,intchildPosition,

booleanisLastChild,ViewconvertView,ViewGroupparent){

//取得要展开的子条目的内容

StringstrArtistName=mArtistList.get(groupPosition).get(childPosition);//mArtistList其实就是一个List<List<String>>

//设置界面

LayoutInflaterinflater=(LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

LinearLayoutll=(LinearLayout)inflater.inflate(R.layout.artistlist_row,null);

TextViewtv=(TextView)ll.findViewById(R.id.txtArtistName);

//设置内容

tv.setText((childPosition+1)+"."+strArtistName);

//设置奇偶行。因为父界面也是奇偶行分割的,为了和父条目的颜色呼应,所以这里要判断一下父条目的颜色。

//即父条目为灰色,那么第一个子条目应该是白色。如果两个都是灰色,就连成一片了。

if(groupPosition%2==0)

ll.setBackgroundColor(childPosition%2==0?Color.argb(250,229,229,229):Color.argb(250,255,255,255));

else

ll.setBackgroundColor(childPosition%2==0?Color.argb(250,255,255,255):Color.argb(250,229,229,229));

returnll;

}

这样就完成了自定义展开前和展开后的效果了。

3.点击某个父条目时,如何收起其他已经展开的条目?

当我们点击某个父条目时,如果这个条目本身是没有展开的,我们需要做两个动作:

1.折叠其他已经展开的条目(为什么?实际应用中需要。不需要的话可以忽略此步)

2.展开这个条目

然而在实现过程中,总是出现异常。

03-1517:18:13.090:ERROR/AndroidRuntime(16202):FATALEXCEPTION:main

03-1517:18:13.090:ERROR/AndroidRuntime(16202):java.lang.IndexOutOfBoundsException:Invalidindex1,sizeis0

真是头晕啊。不知道哪里越界了呢?怎么会size为0呢?跟了好久都不知道为何。看了这篇博文,http://qtcstation.com/2011/03/working-with-the-expandablelistview-part-1/

终于明白了。我也是将折叠其他条目写在onGroupClicl里了.改写在onGroupExpand就没事了。唉唉。

privateExpandableListView.OnGroupExpandListenermGroupExpandListener=newExpandableListView.OnGroupExpandListener(){

publicvoidonGroupExpand(intgroupPosition){

intlen=mAdapter.getGroupCount();

for(inti=0;i<len;i++)

if(i!=groupPosition)

mExpandListView.collapseGroup(i);

}

};

privateExpandableListView.OnGroupClickListenermGroupClickListener=newExpandableListView.OnGroupClickListener(){

publicbooleanonGroupClick(ExpandableListViewparent,Viewv,

intgroupPosition,longid){

//collapseAllExpanded();就是在这里折叠条目出错了。千万不能写在这里。写到onGroupExpand里。

startSearch(groupPosition);//自己写的动态从网上搜索要显示的子条目。改成自己的函数即可。

returnfalse;

}

};

4.旋转时如何让展开的条目保持展开状态并显示它的子条目?

当我们由横屏转竖屏,或竖屏转横屏时,App会有个重新初始化的过程。这时候不做任何处理的话,展开的条目就会丢失它的子条目。这样的显示效果显然是不友好的。旋转后如何保持刚才的内容呢?

下面这篇文章非常有用。

http://stackoverflow.com/questions/4184556/save-and-restore-expanded-collapsed-state-of-an-expandablelistactivity

我们需要用一个数组来记住已经展开的group的id。在onSaveInstanceState函数中记住展开的id。在onRestoreInstanceState函数中恢复这些id,将它们展开。

还要在onStart中检查是否有展开的id,如果有的话,展开这些id。

在onStop中记得储存这些展开的id。

privatelong[]mExpandedIds;//saveexpandedgroupid

@Override

protectedvoidonStart(){

super.onStart();

if(this.mExpandedIds!=null)

restoreExpandedState(mExpandedIds);

}

@Override

protectedvoidonStop(){

super.onStop();

mExpandedIds=getExpandedIds();

}

@Override

protectedvoidonSaveInstanceState(BundleoutState)

{

super.onSaveInstanceState(outState);

this.mExpandedIds=getExpandedIds();

outState.putLongArray("ExpandedIds",this.mExpandedIds);

}

@Override

protectedvoidonRestoreInstanceState(BundlesavedInstanceState)

{

super.onRestoreInstanceState(savedInstanceState);

long[]expandedIds=savedInstanceState.getLongArray("ExpandedIds");

if(expandedIds!=null)

restoreExpandedState(expandedIds);

}

//取得所有展开的group的id

privatelong[]getExpandedIds(){

if(mAdapter!=null)

{

intlength=mAdapter.getGroupCount();

ArrayList<Long>expandedIds=newArrayList<Long>();

for(inti=0;i<length;i++)

{

//判断这个group是否展开。如果展开,就记下来

if(mExpandListView.isGroupExpanded(i))

{

expandedIds.add(mAdapter.getGroupId(i));

}

}

returntoLongArray(expandedIds);

}

else

returnnull;

}

//恢复到展开状态

privatevoidrestoreExpandedState(long[]expandedIds)

{

this.mExpandedIds=expandedIds;

if(mExpandedIds!=null)

{

if(mAdapter!=null)

{

for(inti=0;i<mAdapter.getGroupCount();i++)

{

longid=mAdapter.getGroupId(i);

if(inArray(expandedIds,id))

{

startSearch((int)id);//自己写的动态寻找对应的子条目的函数。改成自己的函数即可。

//如果这个group已经是展开的,那么折叠它,再展开。否则显示的子条目内容在旋转前后不一样。

if(!mExpandListView.expandGroup((int)id))

{

mExpandListView.collapseGroup((int)id);

mExpandListView.expandGroup((int)id);

}

}

}

}

}

}

privatestaticbooleaninArray(long[]array,longelement){

for(longl:array){

if(l==element){

returntrue;

}

}

returnfalse;

}

privatestaticlong[]toLongArray(List<Long>list){

long[]ret=newlong[list.size()];

inti=0;

for(Longe:list)

ret[i++]=e.longValue();

returnret;

}

相关推荐