可折叠列表ExpandableListView

一、认识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,运行程序,可以看到下图所示界面效果。

    点击组的时候,会将其子元素打开,如上图右侧所示,单击其中的列表会弹出消息提示。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值