什么是ExpandableListView
ExpandableListView是扩展的ListView,继承自ListView;ExpandableListView可以实现点击展开列表,再点击收缩回去的效果。
ExpandableListView的使用
首先需要在主布局文件中声明ExpandableListView;
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".TestActivity3"android:orientation="vertical"><!--indicatorLeft:指示符的左边界indicatorRight: 指示符的右边界groupIndicator:设置指示符--><ExpandableListViewandroid:id="@+id/id_elv1"android:layout_width="match_parent"android:layout_height="wrap_content"android:groupIndicator="@drawable/group_indicator"android:indicatorRight="40dp"android:indicatorLeft="10dp"/></LinearLayout>
group_indicator.xml文件,指定不同状态下的样式
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"><!--展开状态--><item android:drawable="@drawable/indicator_expand" android:state_expanded="true">true</item><!--非展开状态--><item android:drawable="@drawable/indicator_collapse"></item>
</selector>
新建Chapter类和ChapterItem类;Chapter代表分组,ChapterItem代表分组中的子选项;
public class Chapter {private int id;private String name;//存储分组中的的子选项private List<ChapterItem> chapterItemList = new ArrayList<>();public Chapter() {}public Chapter(int id, String name) {this.id = id;this.name = name;}//向分组中添加子选项public void addChapterItem(ChapterItem chapterItem) {chapterItem.setPid(getId());chapterItemList.add(chapterItem);}//向分组中添加子选项public void addChapterItem(int cid, String cname) {ChapterItem chapterItem = new ChapterItem(cid, cname);chapterItem.setPid(getId());}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public List<ChapterItem> getChapterItemList() {return chapterItemList;}public void setChapterItemList(List<ChapterItem> chapterItemList) {this.chapterItemList = chapterItemList;}}
ChapterItem类
public class ChapterItem {private String name;//子选项的idprivate int id;//子选项所在分组的idprivate int pid;public ChapterItem() {}public ChapterItem(int id, String name) {this.id = id;this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getId() {return id;}public void setId(int id) {this.id = id;}public int getPid() {return pid;}public void setPid(int pid) {this.pid = pid;}
}
ChapterLab类,使用此类模拟添加数据
public class ChapterLab {public static List<Chapter> getGenerateMockDats() {List<Chapter> datas = new ArrayList<>();Chapter root1 = new Chapter(1, "Android");Chapter root2 = new Chapter(1, "Java");Chapter root3 = new Chapter(1, "C/C++");Chapter root4 = new Chapter(1, "Python");root1.addChapterItem(new ChapterItem(1,"EventBus"));root1.addChapterItem(new ChapterItem(2, "GreenDao"));root1.addChapterItem(new ChapterItem(3, "Gridle"));root1.addChapterItem(new ChapterItem(4, "Spinner"));root2.addChapterItem(new ChapterItem(5,"spring"));root2.addChapterItem(new ChapterItem(6, "springmvc"));root2.addChapterItem(new ChapterItem(7, "mybatis"));root3.addChapterItem(new ChapterItem(8, "hibernate"));root3.addChapterItem(new ChapterItem(9,"STL"));root3.addChapterItem(new ChapterItem(10, "IO"));root3.addChapterItem(new ChapterItem(11, "指针"));root3.addChapterItem(new ChapterItem(12, "结构体"));root4.addChapterItem(new ChapterItem(13,"爬虫"));root4.addChapterItem(new ChapterItem(14, "数据分析"));root4.addChapterItem(new ChapterItem(15, "AI"));root4.addChapterItem(new ChapterItem(16, "人工智能"));datas.add(root1);datas.add(root2);datas.add(root3);datas.add(root4);return datas;}
}
item_parent_chapter.xml文件,父选项布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="horizontal"android:layout_width="match_parent"android:layout_height="56dp"android:background="#86b2f9"><TextViewandroid:id="@+id/id_tv_parent"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center_vertical"android:text="Android"android:textSize="24dp"android:textStyle="bold"/></LinearLayout>
item_child_layoutu.xml文件,子选项布局文件
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="40dp"android:layout_gravity="center_vertical"android:textSize="16dp"android:id="@+id/tv_item"></TextView>
ChapterAdapter数据适配类,这个类继承了BaseExpandableListAdapter并重写了方法,
public class ChapterAdapter extends BaseExpandableListAdapter {//分组的数据private List<Chapter> datas;private LayoutInflater inflater;private Context mContext;public ChapterAdapter(Context context, List<Chapter> datas) {this.datas = datas;this.mContext = context;inflater = LayoutInflater.from(context);}/*** 获取分组的个数* @return*/@Overridepublic int getGroupCount() {return datas.size();}/*** 获取指定分组中子选项的个数* @param groupPosition* @return*/@Overridepublic int getChildrenCount(int groupPosition) {return datas.get(groupPosition).getChapterItemList().size();}/*** 获取指定分组的数据* @param groupPosition* @return*/@Overridepublic Object getGroup(int groupPosition) {return datas.get(groupPosition);}/*** 获取指定分组中的子选项的数据* @param groupPosition* @param childPosition* @return*/@Overridepublic Object getChild(int groupPosition, int childPosition) {return datas.get(groupPosition).getChapterItemList().get(childPosition);}/*** 获取指定分组的ID,这个ID必须是唯一的,可以使用分组的position* @param groupPosition* @return*/@Overridepublic long getGroupId(int groupPosition) {return groupPosition;}/*** 获取子选项的ID,这个ID必须是唯一的,不能使用子条目的position* @param groupPosition* @param childPosition* @return*/@Overridepublic long getChildId(int groupPosition, int childPosition) {return childPosition;}/*** 分组和子选项是否持有稳定的ID,就是说底层数据的改变会不会影响到它们* @return*/@Overridepublic boolean hasStableIds() {return false;}/*** 获取显示指定分组的视图* @param groupPosition* @param isExpanded* @param convertView* @param parent* @return*/@Overridepublic View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {ParentViewHolder parentViewHolder = null;if (convertView == null) {convertView = inflater.inflate(R.layout.item_parent_chapter, parent, false);parentViewHolder = new ParentViewHolder();parentViewHolder.tvName = convertView.findViewById(R.id.id_tv_parent); convertView.setTag(parentViewHolder);} else {parentViewHolder = (ParentViewHolder) convertView.getTag();}Chapter chapter = datas.get(groupPosition);parentViewHolder.tvName.setText(chapter.getName());return convertView;}/*** 获取指定分组中的指定子选项的视图* @param groupPosition* @param childPosition* @param isLastChild* @param convertView* @param parent* @return*/@Overridepublic View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {ChildViewHolder childViewHolder = null;if (convertView == null) {convertView = inflater.inflate(R.layout.item_child_layout, parent, false);childViewHolder = new ChildViewHolder();childViewHolder.tvName = convertView.findViewById(R.id.tv_item);convertView.setTag(childViewHolder);} else {childViewHolder = (ChildViewHolder) convertView.getTag();}ChapterItem chapterItem = datas.get(groupPosition).getChapterItemList().get(childPosition);childViewHolder.tvName.setText(chapterItem.getName());return convertView;}/*** 指定位置上的子元素是否可选中,返回true才会相应点击事件* @param groupPosition* @param childPosition* @return*/@Overridepublic boolean isChildSelectable(int groupPosition, int childPosition) {return true;}public static class ParentViewHolder {TextView tvName; }public static class ChildViewHolder {TextView tvName;}
}
主Activity类
public class TestActivity3 extends AppCompatActivity {private ExpandableListView expandableListView;private ChapterAdapter chapterAdapter;private List<Chapter> dataList;private static final String TAG = "TestActivity3";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_test3);initViews();initEvents();}public void initViews() {expandableListView = findViewById(R.id.id_elv1);dataList = ChapterLab.getGenerateMockDats();chapterAdapter = new ChapterAdapter(this,dataList);expandableListView.setAdapter(chapterAdapter);}public void initEvents() {//设置子选项被点击监听事件expandableListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {@Overridepublic boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {Log.d(TAG, "onChildClick: " + groupPosition + " " + childPosition + " " + id);return false;}});//设置分组选项被点击监听事件expandableListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {@Overridepublic boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {Log.d(TAG, "onGroupClick: " + groupPosition + " " + id);return false;}});//设置折叠时的监听事件expandableListView.setOnGroupCollapseListener(new ExpandableListView.OnGroupCollapseListener() {@Overridepublic void onGroupCollapse(int groupPosition) {Log.d(TAG, "onGroupCollapse: " + groupPosition);}});//设置展开时的监听事件expandableListView.setOnGroupExpandListener(new ExpandableListView.OnGroupExpandListener() {@Overridepublic void onGroupExpand(int groupPosition) {Log.d(TAG, "onGroupExpand: " + groupPosition);}});}}
运行后的效果:
ExpandableListView自带的指示器的效果不好,所以更多情况下我们会选择自定义一个指示器。
修改item_parent_chapter.xml文件,添加一个ImageView控件作为指示符
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="horizontal"android:layout_width="match_parent"android:layout_height="56dp"android:background="#86b2f9"><ImageViewandroid:id="@+id/id_iv_indicator"android:layout_width="24dp"android:layout_gravity="center_vertical"android:layout_marginRight="4dp"android:background="@drawable/group_indicator"android:layout_height="24dp"/><TextViewandroid:id="@+id/id_tv_parent"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center_vertical"android:text="Android"android:textSize="24dp"android:textStyle="bold"/></LinearLayout>
修改主界面布局文件中ExpandableListView的android:groupIndicator属性为@null
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".TestActivity3"android:orientation="vertical"><!--indicatorLeft: 指示符的左边约束距离indicatorRight:指示符的右边约束距离groupIndicator:设置指示--><ExpandableListViewandroid:id="@+id/id_elv1"android:layout_width="match_parent"android:layout_height="wrap_content"android:groupIndicator="@null"android:indicatorRight="40dp"android:indicatorLeft="10dp"/></LinearLayout>
修改group_indicator.xml文件为
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"><!--当使用ImageView来代替indicator时,使用这个--><item android:drawable="@drawable/indicator_expand" android:state_selected="true">true</item><!--非展开状态--><item android:drawable="@drawable/indicator_collapse"></item>
</selector>
修改ChapterAdapter类中的中的getGroupView方法和ParentViewHolder类为以下所示的样子,
@Overridepublic View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {ParentViewHolder parentViewHolder = null;if (convertView == null) {convertView = inflater.inflate(R.layout.item_parent_chapter, parent, false);parentViewHolder = new ParentViewHolder();parentViewHolder.tvName = convertView.findViewById(R.id.id_tv_parent);parentViewHolder.imageView = convertView.findViewById(R.id.id_iv_indicator);convertView.setTag(parentViewHolder);} else {parentViewHolder = (ParentViewHolder) convertView.getTag();}Chapter chapter = datas.get(groupPosition);parentViewHolder.tvName.setText(chapter.getName());parentViewHolder.imageView.setSelected(isExpanded);return convertView;}java
public static class ParentViewHolder {TextView tvName;ImageView imageView;}
运行后效果如图: