android expandablelistview横向,Android 的ExpandableListView使用总结--二级展开树结构

本文详细介绍了Android中ExpandableListView的使用,包括如何创建布局、设置适配器,以及如何实现二级展开的功能。文章还强调了不推荐使用嵌套方式实现多级展开,并提供了自定义指示器的方法,通过改变ImageView资源来实现展开和关闭的状态切换。此外,还提到了点击事件的监听器设置和注意事项,如焦点问题可能导致的点击失效。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、关于ExpandableListView的介绍

ExpandableListView 是默认支持二级展开树形结构,有的朋友喜欢用嵌套的方式实现多级的展开树,我并不建议那样用,写这篇文章就是单纯的总结一下这个空间,以及满足工作中只是简单的二级展开的需求。 后面我会再写一篇关于多层级的展开树,封装成自己的库使用。

2、ExpandableListView 使用

通过一个文件夹结构的例子来讲:

(1)创建布局文件,直接使用ExpandableListView

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical">

android:id="@+id/expand_list"

android:layout_width="match_parent"

android:layout_height="match_parent">

(2)创建ExpandableListView的适配器adapter

这里需要继承BaseExpandableListAdapter,然后实现它的方法

方法虽然挺多,但是好理解,看名字就能知道什么意思。这段代码只是个实例,下面贴出这个文件树结构的代码

public class ExpandListAdapter extends BaseExpandableListAdapter {

private Context mContext;

private List folders; //文件夹数据

private List> childData; //子文件数据

public ExpandListAdapter(Context context,

List folders, List> childData) {

this.mContext = context;

this.folders = folders;

this.childData = childData;

}

@Override

public int getGroupCount() {

return 0;

}

@Override

public int getChildrenCount(int groupPosition) {

return 0;

}

@Override

public Object getGroup(int groupPosition) {

return null;

}

@Override

public Object getChild(int groupPosition, int childPosition) {

return null;

}

@Override

public long getGroupId(int groupPosition) {

return 0;

}

@Override

public long getChildId(int groupPosition, int childPosition) {

return 0;

}

@Override

public boolean hasStableIds() {

return false;

}

@Override

public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {

return null;

}

@Override

public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {

return null;

}

@Override

public boolean isChildSelectable(int groupPosition, int childPosition) {

return false;

}

}

完整的代码如下:

public class ExpandListAdapter extends BaseExpandableListAdapter {

private Context mContext;

private List folders; //文件夹数据

private List> childData; //子文件数据

private SparseArray mIndicators; //创建一个map来存储指示器,然后根据位置改变

public ExpandListAdapter(Context context,

List folders, List> childData) {

this.mContext = context;

this.folders = folders;

this.childData = childData;

this.mIndicators=new SparseArray<>();

}

@Override //获取分组的个数(也就是这里的文件夹个数)

public int getGroupCount() {

return folders.size();

}

@Override//获取指定分组中子选项的个数

public int getChildrenCount(int groupPosition) {

return childData.get(groupPosition).size();

}

@Override//获取指定的分组数据

public Object getGroup(int groupPosition) {

return folders.get(groupPosition);

}

@Override //获取指定分组中指定子选项的数据

public Object getChild(int groupPosition, int childPosition) {

return childData.get(groupPosition).get(childPosition);

}

@Override//获取指定分组的ID, 这个ID必须是唯一的

public long getGroupId(int groupPosition) {

return groupPosition;

}

@Override//获取子选项的ID, 这个ID必须是唯一的

public long getChildId(int groupPosition, int childPosition) {

return childPosition;

}

@Override//分组和子选项是否持有稳定的ID, 就是说底层数据的改变会不会影响到它们。

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(mContext).inflate(R.layout.expadn_list_layout, parent, false);

groupViewHolder = new GroupViewHolder();

groupViewHolder.tv_title = convertView.findViewById(R.id.group_title);

groupViewHolder.iv_icon = convertView.findViewById(R.id.group_icon);

convertView.setTag(groupViewHolder);

}else{

groupViewHolder= (GroupViewHolder) convertView.getTag();

}

groupViewHolder.tv_title.setText(folders.get(groupPosition).getName());

//把要随着状态改变的imageView 指示器添加到集合里面

mIndicators.put(groupPosition,groupViewHolder.iv_icon);

//改变指示器展开或者关闭的显示

setIndicator(groupPosition,isExpanded);

return convertView;

}

@Override//获取显示指定分组中的指定子选项的视图

public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {

ChildViewHolder childViewHolder;

if (convertView==null){

convertView=LayoutInflater.from(mContext).inflate(R.layout.expand_child_layout,parent,false);

childViewHolder=new ChildViewHolder();

childViewHolder.tvTitle=convertView.findViewById(R.id.child_title);

convertView.setTag(childViewHolder);

}else{

childViewHolder= (ChildViewHolder) convertView.getTag();

}

childViewHolder.tvTitle.setText(childData.get(groupPosition).get(childPosition).getName());

return convertView;

}

@Override//指定位置上的子元素是否可选中

public boolean isChildSelectable(int groupPosition, int childPosition) {

return true;

}

class GroupViewHolder {

TextView tv_title;

ImageView iv_icon;

}

class ChildViewHolder {

TextView tvTitle;

}

//设置展开收起的指示器

public void setIndicator(int position,boolean isExpanded){

if (isExpanded){

//从集合中取出指示器的imageview,改变图片的显示

mIndicators.get(position).setImageResource(R.mipmap.file_enter_icon);

}else{

mIndicators.get(position).setImageResource(R.mipmap.file_enter_icon_down);

}

}

}

代码很简单,这里讲一下过程

1、创建adapter类继承BaseExpandableListAdapter ,实现它的方法

2、根据传入的数据填充方法:这里传入了两个集合,一个是文件夹的集合,也就是这个树结构有多少个group;还有一个是元素是结合的集合,存储的是哪一个文件夹下的数据,也就是通过它来取出哪一组,然后从取出的组再取出组里的child

3、跟使用ListView一样,创建Viewholder,然后在getGroupView()和getChildView()这两个方法中找到相应的布局,声明相应的控件。然后优化一下性能方面,具体步骤看代码,和listView一模一样。

4、自定义指示器:(默认的实在是太丑了)

这个也很简单,就是上面文件夹布局(group)中添加一个imageview,具体添加在哪里,看个人喜好,我加载最右边了。通过展开、合上来动态修改显示的图片,待会再看代码,这里只是了解过程

5、为ExpandableListView设置适配器以及隐藏掉默认的指示器

ExpandListAdapter adapter=new ExpandListAdapter(this,folders,childData);

expandList.setGroupIndicator(null);//把指示器设为null

expandList.setAdapter(adapter); //设置adapter

6、这个是硬加的,别忘了要创造数据,这里造假数据。我是创建了一个Bean类

//初始化数据,由于没有后台接口,我们自己造假数据。开发中根据情况自己获取就可以

//模拟文件夹

private void initData() {

folders = new ArrayList<>();

folders.add(new Bean("文件夹一"));

folders.add(new Bean("文件夹二"));

folders.add(new Bean("文件夹三"));

folders.add(new Bean("文件夹四"));

childData = new ArrayList<>();

List list = new ArrayList<>();

list.add(new Bean("A-01.1 Siteplan"));

list.add(new Bean("A-01.2 Basement"));

list.add(new Bean("A-01.3 楼层图"));

list.add(new Bean("A-01.4 First floor"));

childData.add(list);

List list1 = new ArrayList<>();

list1.add(new Bean("A-02.1 图纸一"));

list1.add(new Bean("A-02.2 图纸二"));

childData.add(list1);

List list2 = new ArrayList<>();

list2.add(new Bean("A-03.1 三楼图纸"));

list2.add(new Bean("A-03.2 floor"));

childData.add(list2);

List list3 = new ArrayList<>();

list3.add(new Bean("A-04.1 pager1"));

list3.add(new Bean("A-04.2 pager2"));

list3.add(new Bean("A-04.3 pager3"));

list3.add(new Bean("A-04.4 pager4"));

childData.add(list3);

}

Bean类如下:

public class Bean {

private String name;

public Bean() {

}

public Bean(String name) {

this.name = name;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

}

通过以上的步骤我们基本上就已经实现了二级展开的树结构。

最后把没说完的讲完: 自定义指示器

private SparseArray mIndicators;

//创建一个map来存储指示器,然后根据位置改变

//其实就是在走getGroupView的方法时每次更新时把item的指示器存入,

//然后在根据位置取出来,根据当时的状态来动态改变图片

我是在构造方法中进行初始化的。这个无所谓。顺便贴一下把

public ExpandListAdapter(Context context,

List folders, List> childData) {

this.mContext = context;

this.folders = folders;

this.childData = childData;

this.mIndicators=new SparseArray<>();//初始化

}

然后创建一个方法,来根据位置和展开状态来动态更新图片

//设置展开收起的指示器

public void setIndicator(int position,boolean isExpanded){

if (isExpanded){

//从集合中取出指示器的imageview,改变图片的显示

mIndicators.get(position).setImageResource(R.mipmap.file_enter_icon);

}else{

mIndicators.get(position).setImageResource(R.mipmap.file_enter_icon_down);

}

}

最后千万别忘了一个最重要的操作:

在getGroupView方法中,要把imageView加到集合中,然后调用setIndicator()方法

那一块的代码我也贴一下:

@Override//获取显示指定分组的视图

public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {

GroupViewHolder groupViewHolder;

if (convertView == null) {

convertView = LayoutInflater.from(mContext).inflate(R.layout.expadn_list_layout, parent, false);

groupViewHolder = new GroupViewHolder();

groupViewHolder.tv_title = convertView.findViewById(R.id.group_title);

groupViewHolder.iv_icon = convertView.findViewById(R.id.group_icon);

convertView.setTag(groupViewHolder);

}else{

groupViewHolder= (GroupViewHolder) convertView.getTag();

}

groupViewHolder.tv_title.setText(folders.get(groupPosition).getName());

//把要随着状态改变的imageView 指示器添加到集合里面

mIndicators.put(groupPosition,groupViewHolder.iv_icon);

//改变指示器展开或者关闭的显示

setIndicator(groupPosition,isExpanded);

return convertView;

}

3、点击事件

对于处理 Item 的点击事件,还是要设置监听器,常用的有这几类:

setOnChildClickListener()

setOnGroupClickListener()

setOnGroupCollapseListener()

setOnGroupExpandListener()

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

这个不讲了,具体方法怎么用,直接看顶部的api文档,中文的,啥都有

4、随便聊聊

关于展开、合并的指示器,可以把setIndicator方法的调用放在点击事件里面。

每次点击的时候去实现展开关闭的操作

如果重写点击事件方法,他是默认展开的。。如果我们把return false 变为return true,而不给他指定操作,他就不会展开了。。返回true应该是表示这个点击事件我来做处理,处理完了,但是什么没有做,所以要自己写操作

expandableListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {

@Override

public boolean onGroupClick(ExpandableListView expandableListView, View view, int i, long l) {

boolean groupExpanded = expandableListView.isGroupExpanded(i);

if (groupExpanded) {

expandableListView.collapseGroup(i);

} else {

expandableListView.expandGroup(i, true);

}

adapter.setIndicatorState(i, !groupExpanded);

Log.d("测试", "点击事件方法调用了");

return true;

}

});

下面说的大家不用看,我只是自己记录下

补充一下:我写过一个文件夹的需求,文件夹的图片有四种,选中、不选中、展开并选中、展开不选中--

1、//创建一个集合存储组文件状态

private SparseArray expaned;

expaned=new SparseArray<>();

2、//在getGroupView 方法中把位置和状态存入

expaned.put(i,isExpanded);

3、在修改指示器的方法中加入判断

// 根据分组的展开闭合状态设置指示器

public void setIndicatorState(int groupPosition, boolean isExpanded) {

//先遍历用户操作,根据之前的状态改变指示器(比如:展开了,但没有选中)

for (int i = 0; i

if (expaned.get(i)) {

mIndicators.get(i).setImageResource(R.drawable.foder_open);

} else {

mIndicators.get(i).setImageResource(R.drawable.foder_icon);

}

}

//然后根据点击事件传过来的状态,改变当前item的指示器样式

if (isExpanded) {

mIndicators.get(groupPosition).setImageResource(R.drawable.foder_icon_open);

} else {

mIndicators.get(groupPosition).setImageResource(R.drawable.foder_icon);

}

}

这个方法在点击事件中调用。每次都会遍历一遍存入的集合,然后根据状态更新一下。选中不选中。展开不展开

后来使用的天坑 记录下----浪费了很长时间

首先都知道子布局中如果有抢焦点的控件,比如Button,ExpandablelistView是无法点击的

如果是TextView ,设置了 inputType="" 他妈的居然也无法点击

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值