ExpandableListView的使用简介

第一次接触ExpandableListView控件是在Android source code中的一个开源apk–BluetoothLeGatt,但该apk使用SimpleExpandableListAdapter,加载数据比较麻烦,本例中主要使用BaseExpandableListAdapter

ExpandableListView可以展示两级列表,分组可以单独展开显示子选项。这些选项的数据通过AdapterExpandableListAdapter)来提供数据源。

步骤:

  1. ExpandableListView的布局
  2. 要显示的数据
  3. 一级列表和二级列表的布局
  4. ExtendableListViewAdapter自定义适配器,为ExpandableListView提供数据
  5. Activity调用
  6. 绑定ExpandableListViewExtendableListViewAdapter
  7. 加入点击监听

1. ExpandableListView的布局

1
2
3
4
<ExpandableListView
android:id="@+id/expand_list"
android:layout_width="match_parent"
android:layout_height="match_parent" />

2. 数据

分组的数据是个一维数组,子列表的数据是个二维数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public String[] groups = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J"};
public String[][] children = {
{"A1", "A2"},
{"B1","B2", "B3", "B4"},
{"C1", "C2", "C3"},
{"D1", "D2", "D3", "D4", "D5", "D6"},
{"E1", "E2", "E3", "E4", "E5", "E6", "E1", "E2", "E3", "E4", "E5", "E6"},
{"F1", "F2", "F3", "F4", "F5", "F6"},
{"G1", "G2", "G3", "G4", "G5", "G6"},
{"H1", "H2", "H3", "H4", "H5", "H6"},
{"I1", "I2", "I3", "I4", "I5", "I6"},
{"J1", "J2", "J3", "J4", "J5", "J6","J1", "J2", "J3", "J4", "J5", "J6"},


};

3. 一级列表和二级列表的布局

3.1 一级列表布局

item_layout.xml代码

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?> 
<TextView android:id="@+id/text1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_blue_light"
android:paddingLeft="20dp"
android:textColor="@android:color/white"
android:textSize="30sp"
/>

3.2 二级列表布局

child_item_layout.xml代码

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?> 
<TextView
android:id="@+id/text2"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_below="@+id/text1"
android:layout_alignStart="@+id/text1"
android:paddingLeft="40dp"
android:textSize="20sp"/>

4. 自定义适配器

继承BaseExpandableListAdapter,需要实现以下几个函数

1
2
3
4
5
6
7
8
9
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
class MyExpandableListAdapter extends BaseExpandableListAdapter{
// 获取一级列表的选项个数,即组数
@Override
public int getGroupCount() {
return groups.length;
}

//获取指定一级列表选项的子选项的个数
@Override
public int getChildrenCount(int groupPosition) {
return children[groupPosition].length;
}

//获取组数据
@Override
public Object getGroup(int groupPosition) {
return groups[groupPosition];
}

//获取组的子项数据
@Override
public Object getChild(int groupPosition, int childPosition) {
return children[groupPosition][childPosition];
}

//获取组ID, 这个ID必须是唯一的
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}

//获取子ID, 这个ID必须是唯一的
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}

@Override
public boolean hasStableIds() {
return true;
}

//获取组视图
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
GroupViewHolder groupViewHolder;

if (convertView == null) {
convertView = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_layout, parent, false);
groupViewHolder = new GroupViewHolder();
groupViewHolder.tvTitle = (TextView) convertView.findViewById(R.id.text1);
convertView.setTag(groupViewHolder);
} else {
groupViewHolder = (GroupViewHolder) convertView.getTag();
}
groupViewHolder.tvTitle.setText(groups[groupPosition]);
return convertView;

}

//获取子视图
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
ChildViewHolder childViewHolder;

if (convertView == null) {
convertView = LayoutInflater.from(MainActivity.this).inflate(R.layout.child_item_layout, parent, false);
childViewHolder = new ChildViewHolder();
childViewHolder.tvTitle = (TextView) convertView.findViewById(R.id.text2);
convertView.setTag(childViewHolder);
}
else {
childViewHolder = (ChildViewHolder) convertView.getTag();
}
childViewHolder.tvTitle.setText(children[groupPosition][childPosition]);
return convertView;

}

//子项是否可选
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
}

其中GroupViewHolderChildViewHolder的定义如下,

1
2
3
4
5
6
7
class GroupViewHolder {
TextView tvTitle;
ImageView tvImg;
}
class ChildViewHolder {
TextView tvTitle;
}

5. Activity调用

1
2
3
4
5
6
7
8
9
10
11
public class MainActivity extends Activity {

private ExapandableListView mExpandList;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mExpandList = (ExapandableListView) findViewById(R.id.expand_list);
}
}

6. 绑定ExpandableListViewExtendableListViewAdapter

1
mExpandList.setAdapter(new MyExpandableListAdapter());

7. 加入点击监听

监听函数有:

  1. setOnGroupClickListener
  2. setOnChildClickListener
  3. setOnGroupCollapseListener
  4. setOnGroupExpandListener

它们分别设置单击分组项、单击子选项、分组合并、分组展开的监听器。

7.1 setOnGroupClickListener

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mExpandList.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {

@Override
public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
if (parent.isGroupExpanded(groupPosition)) {
parent.collapseGroup(groupPosition);
} else {
//第二个参数false表示展开时是否触发默认滚动动画
parent.expandGroup(groupPosition, false);
}
//telling the listView we have handled the group click, and don't want the default actions.
return true;
}

});

7.2 setOnChildClickListener

1
2
3
4
5
6
mExpandList.setOnChildClickListener((new ExpandableListView.OnChildClickListener() {

@Override
public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
...
}));

8. Tips

8.1 默认展开所有组

1
2
3
for (int i = 0; i < groups.length; i++) {
mExpandList.expandGroup(i);
}

8.2 取消默认箭头

有两种方法:

  1. 在布局文件xml中添加groupIndicator属性为@null

    1
    android:groupIndicator="@null"
  2. 在代码中设置:

    1
    mExLv.setGroupIndicator(null);

8.3 将箭头改到最右边

  1. 调用的时候加一行android:layoutDirection="rtl"

    1
    2
    3
    <ExpandableListView
    ...
    android:layoutDirection="rtl" />
  2. 然后在一级框父布局里面修改字的走向和位置

    1
    android:gravity=”left”

8.4 将被选中的group置于顶端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
mExpandList.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {

@Override
public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
if (parent.isGroupExpanded(groupPosition)) {
parent.collapseGroup(groupPosition);
} else {
//第二个参数false表示展开时是否触发默认滚动动画
parent.expandGroup(groupPosition, false);
}

+ long flatPostion = parent.getExpandableListPosition(parent.getFirstVisiblePosition());
+ int groupPos = ExpandableListView.getPackedPositionGroup(flatPostion);
+
+ if (groupPosition == groupPos) {
+ parent.setSelectedGroup(groupPos);
+ }
//telling the listView we have handled the group click, and don't want the default actions.
return true;
}

});

8.5 改变一级菜单箭头的位置

1
mExpandList.setIndicatorBounds(60, 150);

定义表示箭头的矩形框的位置(左,右)

9. 参考文献

Android中的Selector的用法
设置ExpandableListView左边的箭头
Android之ExpandableListView的各种效果(默认展开不合闭,自定义父栏目及箭头控制)