在 Android 开发中,列表展示是一种非常常见的交互形式。而 ExpandableListView 作为一种特殊的列表控件,它允许我们创建具有分组功能的列表,每个分组下还可以包含多个子项,并且分组可以展开和收缩,这大大增强了数据展示的灵活性和可读性。本文将详细介绍 ExpandableListView 的用法,从基本概念到具体实现,再到高级应用,帮助你全面掌握这一控件。
ExpandableListView 继承自 ListView,是 Android 提供的一种可展开的列表视图。它主要由分组(Group)和子项(Child)两部分组成。每个分组可以包含多个子项,用户可以通过点击分组来展开或收缩其对应的子项列表。这种控件适用于需要对数据进行分类展示的场景,例如联系人列表按照字母分组、商品列表按照类别分组等。
首先,我们需要在布局文件中添加 ExpandableListView 控件。以下是一个简单的示例:
1 2 3 4 5 6 7 8 9 10 |
<!-- 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"> <ExpandableListView android:id="@+id/expandableListView" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout> |
在这个布局中,我们创建了一个垂直的线性布局,并在其中添加了一个 ExpandableListView,其宽度和高度都设置为 match_parent,以填充整个父布局。
为了展示分组和子项的数据,我们需要创建相应的数据模型类。假设我们要展示一个水果分类列表,每个分类下有不同的水果,我们可以创建如下的数据模型类:
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 |
// 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 类用于表示子项信息,包含一个子项名称。
ExpandableListView 需要使用适配器来将数据绑定到视图上。我们可以继承 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 |
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; } @Override public int getGroupCount() { return groupList.size(); } @Override public int getChildrenCount(int groupPosition) { return childList.get(groupPosition).size(); } @Override public Object getGroup(int groupPosition) { return groupList.get(groupPosition); } @Override public Object getChild(int groupPosition, int childPosition) { return childList.get(groupPosition).get(childPosition); } @Override public long getGroupId(int groupPosition) { return groupPosition; } @Override public long getChildId(int groupPosition, int childPosition) { return childPosition; } @Override public boolean hasStableIds() { return false; } @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(android.R.layout.simple_expandable_list_item_1, null); } TextView groupTextView = convertView.findViewById(android.R.id.text1); 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(android.R.layout.simple_list_item_1, null); } TextView childTextView = convertView.findViewById(android.R.id.text1); childTextView.setText(childModel.getChildName()); return convertView; } @Override public boolean isChildSelectable(int groupPosition, int childPosition) { return true; } } |
在这个适配器中,我们需要实现一系列的方法:
在 Activity 中,我们需要初始化数据、创建适配器并将其设置给 ExpandableListView。以下是示例代码:
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 |
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; @Override protected 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。
我们可以为 ExpandableListView 添加分组和子项的点击事件监听器,以实现相应的交互逻辑。以下是示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
// 在 onCreate 方法中添加以下代码 expandableListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() { @Override public 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() { @Override public 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; } }); |
在分组点击事件监听器中,我们可以获取点击的分组对象并进行相应的处理;在子项点击事件监听器中,我们可以获取点击的子项对象并进行相应的处理。
除了使用系统提供的简单布局,我们还可以自定义分组和子项的视图,以实现更丰富的界面效果。以下是一个自定义视图的示例:
自定义分组布局文件 group_item.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<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"> <ImageView android:id="@+id/group_icon" android:layout_width="24dp" android:layout_height="24dp" android:src="@mipmap/ic_launcher" /> <TextView android: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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<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"> <ImageView android:id="@+id/child_icon" android:layout_width="24dp" android:layout_height="24dp" android:src="@mipmap/ic_launcher" /> <TextView android:id="@+id/child_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:textSize="16sp" /> </LinearLayout> |
修改适配器中的 getGroupView 和 getChildView 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
@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; } |
通过自定义视图,我们可以在分组和子项中添加更多的控件,如图片、按钮等,从而实现更复杂的界面效果。
ExpandableListView 是 Android 中一个非常实用的列表控件,它可以帮助我们实现具有分组功能的列表展示。通过本文的介绍,你应该已经掌握了 ExpandableListView 的基本用法,包括布局文件的添加、数据模型的创建、适配器的实现、点击事件的处理以及自定义视图的应用。在实际开发中,你可以根据具体需求对其进行进一步的扩展和优化,以满足不同的业务场景。希望本文对你有所帮助,祝你在 Android 开发中取得更好的成果!