一、认识ExpandableListView
ExpandableListView 是 ListView 的子类,它在普通ListView的基础上进行了扩展,它把应用中的列表项分为几组,每组里又可包含多个列表项。
ExpandableListView的用法与普通 ListView的用法非常相似,只是 ExpandableListView所显示的列表项应 该由 ExpandableListAdapter 提供。ExpandableListAdapter 也是一个接口,该接口下提供的类继承关系图如下图所示。
与Adapter类似的是,实现 ExpandableListAdapter也有如下三种常用方式。
-
扩展 BaseExpandableListAdapter 实现 ExpandableListAdapter。
-
使用 SimpleExpandableListAdapter 将两个 List 集合包装成 ExpandableListAdapter。
-
使用 SimpleCursorTreeAdapter 将 Cursor 中的数据包装成 SimpleCursorTreeAdapter。
ExpandableListView支持的常用XML属性如下:
-
android:childDivider:指定各组内子类表项之间的分隔条,图片不会完全显示, 分离子列表项的是一条直线。
-
android:childIndicator:显示在子列表旁边的Drawable对象,可以是一个图像。
-
android:childIndicatorEnd:子列表项指示符的结束约束位置。
-
android:childIndicatorLeft:子列表项指示符的左边约束位置。
-
android:childIndicatorRight:子列表项指示符的右边约束位置。
-
android:childIndicatorStart:子列表项指示符的开始约束位置。
-
android:groupIndicator:显示在组列表旁边的Drawable对象,可以是一个图像。
-
android:indicatorEnd:组列表项指示器的结束约束位置。
-
android:indicatorLeft:组列表项指示器的左边约束位置。
-
android:indicatorRight:组列表项指示器的右边约束位置。
-
android:indicatorStart:组列表项指示器的开始约束位置。
二、ExpandableListView 示例
接下来通过一个简单的示例程序来学习ExpandableListView的使用方法。
继续使用WidgetSample工程的listviewsample模块,在app/main/res/layout/目录下创建expandlist_layout.xml文件,在其中填充如下代码片段:
[代码]xml代码:
01
02
03
04
05
06
07
08
09
10
|
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
<
RelativeLayout
xmlns:android
=
"http://schemas.android.com/apk/res/android"
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
>
<
ExpandableListView
android:id
=
"@+id/expendlist"
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
/>
</
RelativeLayout
>
|
在res/layout/目录创建expendlist_group.xml文件,代码如下:
[代码]xml代码:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
<
RelativeLayout
xmlns:android
=
"http://schemas.android.com/apk/res/android"
android:layout_width
=
"match_parent"
android:layout_height
=
"70dp"
android:gravity
=
"center_vertical"
android:padding
=
"10dp"
>
<
ImageView
android:id
=
"@+id/group_img"
android:layout_width
=
"36dp"
android:layout_height
=
"36dp"
android:layout_centerVertical
=
"true"
android:layout_marginLeft
=
"20dp"
android:src
=
"@drawable/group_close"
/>
<
TextView
android:id
=
"@+id/groupname_tv"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:layout_centerVertical
=
"true"
android:layout_toRightOf
=
"@+id/group_img"
android:layout_marginLeft
=
"10dp"
android:text
=
"张三"
android:textSize
=
"18sp"
/>
</
RelativeLayout
>
|
在res/layout/目录创建expendlist_item.xml文件,代码如下:
[代码]xml代码:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
<?
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
=
"70dp"
android:orientation
=
"horizontal"
android:gravity
=
"center_vertical"
android:padding
=
"10dp"
>
<
ImageView
android:id
=
"@+id/icon_img"
android:layout_width
=
"48dp"
android:layout_height
=
"48dp"
android:layout_marginLeft
=
"20dp"
android:src
=
"@drawable/item"
/>
<
LinearLayout
android:layout_width
=
"match_parent"
android:layout_height
=
"60dp"
android:gravity
=
"center_vertical"
android:orientation
=
"vertical"
>
<
TextView
android:id
=
"@+id/itemname_tv"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:textSize
=
"22sp"
android:textStyle
=
"bold"
android:text
=
"李大钊"
/>
<
TextView
android:id
=
"@+id/info_tv"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:layout_marginTop
=
"10dp"
android:text
=
"今天是个好日子啊!"
/>
</
LinearLayout
>
</
LinearLayout
>
|
新建MyExpandableListViewAdapter类,继承ExpandableListViewAdapter,并重写其方法,代码如下:
[代码]java代码:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
|
package
com.jinyu.cqkxzsxy.android.listviewsample.adapter;
import
android.content.Context;
import
android.view.LayoutInflater;
import
android.view.View;
import
android.view.ViewGroup;
import
android.widget.BaseExpandableListAdapter;
import
android.widget.ImageView;
import
android.widget.TextView;
import
com.jinyu.cqkxzsxy.android.listviewsample.R;
import
java.util.List;
/**
* @创建者 鑫鱻
* @描述 Android零基础入门到精通系列教程,欢迎关注微信公众号ShareExpert
*/
public
class
MyExpandableListViewAdapter
extends
BaseExpandableListAdapter {
private
Context mContext =
null
;
private
List<string> mGroupList =
null
;
private
List<list<string>> mItemList =
null
;
public
MyExpandableListViewAdapter(Context context, List<string> groupList,
List<list<string>> itemList) {
this
.mContext = context;
this
.mGroupList = groupList;
this
.mItemList = itemList;
}
/**
* 获取组的个数
*
* @return
* @see android.widget.ExpandableListAdapter#getGroupCount()
*/
@Override
public
int
getGroupCount() {
return
mGroupList.size();
}
/**
* 获取指定组中的子元素个数
*
* @param groupPosition
* @return
* @see android.widget.ExpandableListAdapter#getChildrenCount(int)
*/
@Override
public
int
getChildrenCount(
int
groupPosition) {
return
mItemList.get(groupPosition).size();
}
/**
* 获取指定组中的数据
*
* @param groupPosition
* @return
* @see android.widget.ExpandableListAdapter#getGroup(int)
*/
@Override
public
String getGroup(
int
groupPosition) {
return
mGroupList.get(groupPosition);
}
/**
* 获取指定组中的指定子元素数据。
*
* @param groupPosition
* @param childPosition
* @return
* @see android.widget.ExpandableListAdapter#getChild(int, int)
*/
@Override
public
String getChild(
int
groupPosition,
int
childPosition) {
return
mItemList.get(groupPosition).get(childPosition);
}
/**
* 获取指定组的ID,这个组ID必须是唯一的
*
* @param groupPosition
* @return
* @see android.widget.ExpandableListAdapter#getGroupId(int)
*/
@Override
public
long
getGroupId(
int
groupPosition) {
return
groupPosition;
}
/**
* 获取指定组中的指定子元素ID
*
* @param groupPosition
* @param childPosition
* @return
* @see android.widget.ExpandableListAdapter#getChildId(int, int)
*/
@Override
public
long
getChildId(
int
groupPosition,
int
childPosition) {
return
childPosition;
}
/**
* 获取显示指定组的视图对象
*
* @param groupPosition 组位置
* @param isExpanded 该组是展开状态还是伸缩状态
* @param convertView 重用已有的视图对象
* @param parent 返回的视图对象始终依附于的视图组
* @return
* @see android.widget.ExpandableListAdapter#getGroupView(int, boolean, android.view.View,
* android.view.ViewGroup)
*/
@Override
public
View getGroupView(
int
groupPosition,
boolean
isExpanded, View convertView,
ViewGroup parent) {
GroupHolder groupHolder =
null
;
if
(convertView ==
null
) {
convertView = LayoutInflater.from(mContext).inflate(R.layout.expendlist_group,
null
);
groupHolder =
new
GroupHolder();
groupHolder.groupNameTv = (TextView) convertView.findViewById(R.id.groupname_tv);
groupHolder.groupImg = (ImageView) convertView.findViewById(R.id.group_img);
convertView.setTag(groupHolder);
}
else
{
groupHolder = (GroupHolder) convertView.getTag();
}
if
(isExpanded) {
groupHolder.groupImg.setImageResource(R.drawable.group_open);
}
else
{
groupHolder.groupImg.setImageResource(R.drawable.group_close);
}
groupHolder.groupNameTv.setText(mGroupList.get(groupPosition));
return
convertView;
}
/**
* 获取一个视图对象,显示指定组中的指定子元素数据。
*
* @param groupPosition 组位置
* @param childPosition 子元素位置
* @param isLastChild 子元素是否处于组中的最后一个
* @param convertView 重用已有的视图(View)对象
* @param parent 返回的视图(View)对象始终依附于的视图组
* @return
* @see android.widget.ExpandableListAdapter#getChildView(int, int, boolean, android.view.View,
* android.view.ViewGroup)
*/
@Override
public
View getChildView(
int
groupPosition,
int
childPosition,
boolean
isLastChild,
View convertView, ViewGroup parent) {
ItemHolder itemHolder =
null
;
if
(convertView ==
null
) {
convertView = LayoutInflater.from(mContext).inflate(R.layout.expendlist_item,
null
);
itemHolder =
new
ItemHolder();
itemHolder.nameTv = (TextView) convertView.findViewById(R.id.itemname_tv);
itemHolder.iconImg = (ImageView) convertView.findViewById(R.id.icon_img);
convertView.setTag(itemHolder);
}
else
{
itemHolder = (ItemHolder) convertView.getTag();
}
itemHolder.nameTv.setText(mItemList.get(groupPosition).get(childPosition));
itemHolder.iconImg.setBackgroundResource(R.drawable.item);
return
convertView;
}
/**
* 组和子元素是否持有稳定的ID,也就是底层数据的改变不会影响到它们。
*
* @return
* @see android.widget.ExpandableListAdapter#hasStableIds()
*/
@Override
public
boolean
hasStableIds() {
return
true
;
}
/**
* 是否选中指定位置上的子元素。
*
* @param groupPosition
* @param childPosition
* @return
* @see android.widget.ExpandableListAdapter#isChildSelectable(int, int)
*/
@Override
public
boolean
isChildSelectable(
int
groupPosition,
int
childPosition) {
return
true
;
}
class
GroupHolder {
public
TextView groupNameTv;
public
ImageView groupImg;
}
class
ItemHolder {
public
ImageView iconImg;
public
TextView nameTv;
}
}
</list<string></string></list<string></string>
|
上面程序的关键代码就是扩展BaseExpandableListAdapter来实现ExpandableListAdapter, 当扩展BaseExpandableListAdapter时,关键是实现如下4个方法。
-
getGroupCount():该方法返回包含的组列表项的数量。
-
getGroupView():该方法返回的View对象将作为组列表项。
-
getChildrenCount():该方法返回特定组所包含的子列表项的数量。
-
getChildView():该方法返回的View对象将作为特定组、特定位置的子列表项。
新建ExpandableListActivity.java文件,加载上面新建的布局文件,具体代码如下:
[代码]java代码:
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
|
package
com.jinyu.cqkxzsxy.android.listviewsample;
import
android.os.Bundle;
import
android.support.v7.app.AppCompatActivity;
import
android.view.View;
import
android.widget.ExpandableListView;
import
android.widget.Toast;
import
com.jinyu.cqkxzsxy.android.listviewsample.adapter.MyExpandableListViewAdapter;
import
java.util.ArrayList;
import
java.util.List;
public
class
ExpandableListActivity
extends
AppCompatActivity {
private
ExpandableListView mExpandableListView =
null
;
// 列表数据
private
List<string> mGroupNameList =
null
;
private
List<list<string>> mItemNameList =
null
;
// 适配器
private
MyExpandableListViewAdapter mAdapter =
null
;
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.expandlist_layout);
// 获取组件
mExpandableListView = (ExpandableListView) findViewById(R.id.expendlist);
mExpandableListView.setGroupIndicator(
null
);
// 初始化数据
initData();
// 为ExpandableListView设置Adapter
mAdapter =
new
MyExpandableListViewAdapter(
this
, mGroupNameList, mItemNameList);
mExpandableListView.setAdapter(mAdapter);
// 监听组点击
mExpandableListView.setOnGroupClickListener(
new
ExpandableListView.OnGroupClickListener() {
@Override
public
boolean
onGroupClick(ExpandableListView parent, View v,
int
groupPosition,
long
id) {
if
(mGroupNameList.get(groupPosition).isEmpty()) {
return
true
;
}
return
false
;
}
});
// 监听每个分组里子控件的点击事件
mExpandableListView.setOnChildClickListener(
new
ExpandableListView.OnChildClickListener() {
@Override
public
boolean
onChildClick(ExpandableListView parent, View v,
int
groupPosition,
int
childPosition,
long
id) {
Toast.makeText(ExpandableListActivity.
this
,
mAdapter.getGroup(groupPosition) +
":"
+ mAdapter.getChild(groupPosition, childPosition) ,
Toast.LENGTH_SHORT).show();
return
false
;
}
});
}
// 初始化数据
private
void
initData(){
// 组名
mGroupNameList =
new
ArrayList<string>();
mGroupNameList.add(
"历代帝王"
);
mGroupNameList.add(
"华坛明星"
);
mGroupNameList.add(
"国外明星"
);
mGroupNameList.add(
"政坛人物"
);
mItemNameList =
new
ArrayList<list<string>>();
// 历代帝王组
List<string> itemList =
new
ArrayList<string>();
itemList.add(
"唐太宗李世民"
);
itemList.add(
"秦始皇嬴政"
);
itemList.add(
"汉武帝刘彻"
);
itemList.add(
"明太祖朱元璋"
);
itemList.add(
"宋太祖赵匡胤"
);
mItemNameList.add(itemList);
// 华坛明星组
itemList =
new
ArrayList<string>();
itemList.add(
"范冰冰 "
);
itemList.add(
"梁朝伟"
);
itemList.add(
"谢霆锋"
);
itemList.add(
"章子怡"
);
itemList.add(
"杨颖"
);
itemList.add(
"张柏芝"
);
mItemNameList.add(itemList);
// 国外明星组
itemList =
new
ArrayList<string>();
itemList.add(
"安吉丽娜•朱莉"
);
itemList.add(
"艾玛•沃特森"
);
itemList.add(
"朱迪•福斯特"
);
mItemNameList.add(itemList);
// 政坛人物组
itemList =
new
ArrayList<string>();
itemList.add(
"唐纳德•特朗普"
);
itemList.add(
"金正恩"
);
itemList.add(
"奥巴马"
);
itemList.add(
"普京"
);
mItemNameList.add(itemList);
}
}
</string></string></string></string></string></list<string></string></list<string></string>
|
上述代码为ExpandableListView设置Adapter,并为ExpandableListView设置事件监听器。
修改程序启动的Activity,运行程序,可以看到下图所示界面效果。
点击组的时候,会将其子元素打开,如上图右侧所示,单击其中的列表会弹出消息提示。