Android ExpandableListView 详细用法全解析

news/2025/2/8 15:57:54/

引言

在 Android 开发中,列表展示是一种非常常见的交互形式。而 ExpandableListView 作为一种特殊的列表控件,它允许我们创建具有分组功能的列表,每个分组下还可以包含多个子项,并且分组可以展开和收缩,这大大增强了数据展示的灵活性和可读性。本文将详细介绍 ExpandableListView 的用法,从基本概念到具体实现,再到高级应用,帮助你全面掌握这一控件。

1. ExpandableListView 基本概念

ExpandableListView 继承自 ListView,是 Android 提供的一种可展开的列表视图。它主要由分组(Group)和子项(Child)两部分组成。每个分组可以包含多个子项,用户可以通过点击分组来展开或收缩其对应的子项列表。这种控件适用于需要对数据进行分类展示的场景,例如联系人列表按照字母分组、商品列表按照类别分组等。

2. 布局文件中添加 ExpandableListView

首先,我们需要在布局文件中添加 ExpandableListView 控件。以下是一个简单的示例:

<!-- activity_main.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><ExpandableListViewandroid:id="@+id/expandableListView"android:layout_width="match_parent"android:layout_height="match_parent" />
</LinearLayout>

在这个布局中,我们创建了一个垂直的线性布局,并在其中添加了一个 ExpandableListView,其宽度和高度都设置为 match_parent,以填充整个父布局。

3. 创建数据模型

为了展示分组和子项的数据,我们需要创建相应的数据模型类。假设我们要展示一个水果分类列表,每个分类下有不同的水果,我们可以创建如下的数据模型类:

// GroupModel.java
public class GroupModel {private String groupName;public GroupModel(String groupName) {this.groupName = groupName;}public String getGroupName() {return groupName;}public void setGroupName(String groupName) {this.groupName = groupName;}
}// ChildModel.java
public class ChildModel {private String childName;public ChildModel(String childName) {this.childName = childName;}public String getChildName() {return childName;}public void setChildName(String childName) {this.childName = childName;}
}

GroupModel 类用于表示分组信息,包含一个分组名称;ChildModel 类用于表示子项信息,包含一个子项名称。

4. 创建适配器

ExpandableListView 需要使用适配器来将数据绑定到视图上。我们可以继承 BaseExpandableListAdapter 类来创建自定义适配器。以下是一个示例:

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.TextView;import java.util.List;public class MyExpandableListAdapter extends BaseExpandableListAdapter {private Context context;private List<GroupModel> groupList;private List<List<ChildModel>> childList;public MyExpandableListAdapter(Context context, List<GroupModel> groupList, List<List<ChildModel>> childList) {this.context = context;this.groupList = groupList;this.childList = childList;}@Overridepublic int getGroupCount() {return groupList.size();}@Overridepublic int getChildrenCount(int groupPosition) {return childList.get(groupPosition).size();}@Overridepublic Object getGroup(int groupPosition) {return groupList.get(groupPosition);}@Overridepublic Object getChild(int groupPosition, int childPosition) {return childList.get(groupPosition).get(childPosition);}@Overridepublic long getGroupId(int groupPosition) {return groupPosition;}@Overridepublic long getChildId(int groupPosition, int childPosition) {return childPosition;}@Overridepublic boolean hasStableIds() {return false;}@Overridepublic View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {GroupModel groupModel = (GroupModel) getGroup(groupPosition);if (convertView == null) {LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);convertView = inflater.inflate(android.R.layout.simple_expandable_list_item_1, null);}TextView groupTextView = convertView.findViewById(android.R.id.text1);groupTextView.setText(groupModel.getGroupName());return convertView;}@Overridepublic View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {ChildModel childModel = (ChildModel) getChild(groupPosition, childPosition);if (convertView == null) {LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);convertView = inflater.inflate(android.R.layout.simple_list_item_1, null);}TextView childTextView = convertView.findViewById(android.R.id.text1);childTextView.setText(childModel.getChildName());return convertView;}@Overridepublic boolean isChildSelectable(int groupPosition, int childPosition) {return true;}
}

在这个适配器中,我们需要实现一系列的方法:

  • getGroupCount():返回分组的数量。
  • getChildrenCount(int groupPosition):返回指定分组下子项的数量。
  • getGroup(int groupPosition):返回指定位置的分组对象。
  • getChild(int groupPosition, int childPosition):返回指定分组下指定位置的子项对象。
  • getGroupId(int groupPosition) 和 getChildId(int groupPosition, int childPosition):分别返回分组和子项的 ID。
  • getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent):创建并返回分组的视图。
  • getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent):创建并返回子项的视图。
  • isChildSelectable(int groupPosition, int childPosition):指定子项是否可以被选中。

5. 在 Activity 中使用 ExpandableListView

在 Activity 中,我们需要初始化数据、创建适配器并将其设置给 ExpandableListView。以下是示例代码:

import android.os.Bundle;
import android.widget.ExpandableListView;import androidx.appcompat.app.AppCompatActivity;import java.util.ArrayList;
import java.util.List;public class MainActivity extends AppCompatActivity {private ExpandableListView expandableListView;private MyExpandableListAdapter adapter;private List<GroupModel> groupList;private List<List<ChildModel>> childList;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);expandableListView = findViewById(R.id.expandableListView);// 初始化数据initData();// 创建适配器adapter = new MyExpandableListAdapter(this, groupList, childList);// 设置适配器expandableListView.setAdapter(adapter);}private void initData() {groupList = new ArrayList<>();childList = new ArrayList<>();// 添加分组数据GroupModel group1 = new GroupModel("热带水果");GroupModel group2 = new GroupModel("温带水果");groupList.add(group1);groupList.add(group2);// 为每个分组添加子项数据List<ChildModel> childList1 = new ArrayList<>();childList1.add(new ChildModel("香蕉"));childList1.add(new ChildModel("芒果"));childList.add(childList1);List<ChildModel> childList2 = new ArrayList<>();childList2.add(new ChildModel("苹果"));childList2.add(new ChildModel("梨"));childList.add(childList2);}
}

在 onCreate 方法中,我们首先获取 ExpandableListView 控件的引用,然后调用 initData 方法初始化数据,接着创建适配器并将其设置给 ExpandableListView。

6. 处理分组和子项的点击事件

我们可以为 ExpandableListView 添加分组和子项的点击事件监听器,以实现相应的交互逻辑。以下是示例代码:

// 在 onCreate 方法中添加以下代码
expandableListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {@Overridepublic boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {// 处理分组点击事件GroupModel groupModel = (GroupModel) adapter.getGroup(groupPosition);String groupName = groupModel.getGroupName();// 这里可以添加自定义的逻辑,比如弹出提示框显示分组名称return false;}
});expandableListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {@Overridepublic boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {// 处理子项点击事件ChildModel childModel = (ChildModel) adapter.getChild(groupPosition, childPosition);String childName = childModel.getChildName();// 这里可以添加自定义的逻辑,比如跳转到详情页面return false;}
});

在分组点击事件监听器中,我们可以获取点击的分组对象并进行相应的处理;在子项点击事件监听器中,我们可以获取点击的子项对象并进行相应的处理。

7. 高级应用:自定义视图

除了使用系统提供的简单布局,我们还可以自定义分组和子项的视图,以实现更丰富的界面效果。以下是一个自定义视图的示例:
自定义分组布局文件 group_item.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:padding="10dp"><ImageViewandroid:id="@+id/group_icon"android:layout_width="24dp"android:layout_height="24dp"android:src="@mipmap/ic_launcher" /><TextViewandroid:id="@+id/group_name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="10dp"android:textSize="18sp" />
</LinearLayout>

自定义子项布局文件 child_item.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:padding="10dp"><ImageViewandroid:id="@+id/child_icon"android:layout_width="24dp"android:layout_height="24dp"android:src="@mipmap/ic_launcher" /><TextViewandroid:id="@+id/child_name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="10dp"android:textSize="16sp" />
</LinearLayout>

修改适配器中的 getGroupView 和 getChildView 方法

@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {GroupModel groupModel = (GroupModel) getGroup(groupPosition);if (convertView == null) {LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);convertView = inflater.inflate(R.layout.group_item, null);}ImageView groupIcon = convertView.findViewById(R.id.group_icon);TextView groupTextView = convertView.findViewById(R.id.group_name);groupTextView.setText(groupModel.getGroupName());return convertView;
}@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {ChildModel childModel = (ChildModel) getChild(groupPosition, childPosition);if (convertView == null) {LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);convertView = inflater.inflate(R.layout.child_item, null);}ImageView childIcon = convertView.findViewById(R.id.child_icon);TextView childTextView = convertView.findViewById(R.id.child_name);childTextView.setText(childModel.getChildName());return convertView;
}

通过自定义视图,我们可以在分组和子项中添加更多的控件,如图片、按钮等,从而实现更复杂的界面效果。

8. 总结

ExpandableListView 是 Android 中一个非常实用的列表控件,它可以帮助我们实现具有分组功能的列表展示。通过本文的介绍,你应该已经掌握了 ExpandableListView 的基本用法,包括布局文件的添加、数据模型的创建、适配器的实现、点击事件的处理以及自定义视图的应用。在实际开发中,你可以根据具体需求对其进行进一步的扩展和优化,以满足不同的业务场景。希望本文对你有所帮助,祝你在 Android 开发中取得更好的成果!


http://www.ppmy.cn/news/1570357.html

相关文章

【梦想终会实现】Linux驱动学习5

加油加油坚持住&#xff01; 1、 Linux驱动模型&#xff1a;驱动模型即将各模型中共有的部分抽象成C结构体。Linux2.4版本前无驱动模型的概念&#xff0c;每个驱动写的代码因人而异&#xff0c;随后为规范书写方式&#xff0c;发明了驱动模型&#xff0c;即提取公共信息组成一…

在 Elastic 中实施聚类工作流以提升搜索相关性

作者&#xff1a;来自 Elastic Gus Carlock 及 Kirti Sodhi 我们演示了如何利用 OpenAI text-ada-002 向量将自定义聚类模型集成到 Elastic Stack 中&#xff0c;从而简化 Elastic 生态系统内的工作流程。 在本文中&#xff0c;我们将演示如何通过利用示例文本数据集将自定义聚…

【Linux开发工具】包管理器yum和文本编辑器vim

目录 一、前言 二、Linux中的软件商店yum 1.三种安装软件的方式 2.yum的使用方法 三、Linux中的编辑器vim 1.vim的三种模式及切换 2.命令模式详解 3.底行模式详解 4.替换模式 5.视图模式 6.vim下的多线程操作 7.vim的配置 四、总结 一、前言 在初步认识了Linux的基…

windows11上,使用pipx安装Poetry,Poetry的安装路径是什么?

当使用 pipx 安装 Poetry 时&#xff0c;pipx 会将 Poetry 安装到一个独立的虚拟环境中&#xff0c;并将其可执行文件链接到一个集中的目录中。以下是 pipx 安装 Poetry 时的路径信息&#xff1a; 1. Poetry 的安装路径 pipx 会为每个工具&#xff08;如 Poetry&#xff09;创…

k8sollama部署deepseek-R1模型,内网无坑

这是目录 linux下载ollama模型文件下载到本地,打包迁移到k8s等无网络环境使用下载打包ollama镜像非k8s环境使用k8s部署访问方式非ollama运行deepseek模型linux下载ollama 下载后可存放其他服务器 curl -L https://ollama.com/download/ollama-linux-amd64.tgz -o ollama-linu…

开源CodeGPT + DeepSeek-R1 是否可以替代商业付费代码辅助工具

开源CodeGPT + DeepSeek-R1 是否可以替代商业付费代码辅助工具 背景与研究目的 在快速发展的软件开发领域,代码辅助工具已成为提高开发效率和质量的关键。然而,商业付费工具如通义灵码和腾讯AI代码助手,尽管功能强大,但其高昂的成本和许可证限制,使得许多企业寻求更具吸…

K8S组件架构

master节点上 kube-apiserver&#xff1a;服务端&#xff0c;提供k8s api接口服务&#xff0c;接受外部请求管理和控制整个集群。 kube-secheduler&#xff1a;调度计算&#xff0c;负责根据资源需求和约束条件&#xff0c;将pod调度到合适的主机上。 kube-controller-manag…

TCN时间卷积神经网络多变量多步光伏功率预测(Matlab)

代码下载&#xff1a;TCN时间卷积神经网络多变量多步光伏功率预测&#xff08;Matlab&#xff09; TCN时间卷积神经网络多变量多步光伏功率预测 一、引言 1.1、研究背景和意义 随着全球能源危机的加剧和环保意识的提升&#xff0c;可再生能源&#xff0c;尤其是太阳能&…