当你需要在Android应用中展示大量数据时,列表是一个常见的UI组件。但是,有时候数据量大到一定程度,简单的列表就无法满足需求。这时,分组列表就成为了一个很好的选择。ExpandableListView 是 Android 中的一个强大的组件,它可以帮助你轻松地实现列表分组的功能。在这篇博客中,我们将探讨如何使用 ExpandableListView 来实现列表的分组功能,让你的应用更加高效和易于管理。我们将深入讨论 ExpandableListView 的基本用法、如何为列表添加分组、以及如何自定义列表项的外观和交互效果。通过本文的学习,你将能够掌握使用 ExpandableListView 构建分组列表的技巧,为你的应用增添更多交互和可用性。
先看效果图:
(可能UI有点拉哈哈哈😂,但是不影响功能实现)
那么这种效果是如何实现的,其实代码也是比较简单,其中数据部分根据自己需求进行更改。
直接看代码:
MainActivity.java(查询数据部分可以根据需求进行更改例如进行网络获取)
public class MainActivity extends AppCompatActivity {
private List<Data> datalist = new ArrayList<>();
private HashMap<String, List<Data>> postMap = new HashMap<>();
private ExpandableListView expandableListView;
private ParentAdapter expandableListAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
expandableListView=findViewById(R.id.expandableListView);
querydata();//查询数据
}
/**
* 模拟网络查询数据
*/
private void querydata(){
datalist.add(new Data("中国", "四川"));
datalist.add(new Data("中国", "河南"));
datalist.add(new Data("中国", "广东"));
datalist.add(new Data("中国", "浙江"));
datalist.add(new Data("美国", "纽约"));
datalist.add(new Data("美国", "加利福尼亚"));
datalist.add(new Data("美国", "德克萨斯"));
datalist.add(new Data("英国", "伦敦"));
datalist.add(new Data("英国", "曼彻斯特"));
datalist.add(new Data("法国", "巴黎"));
groups();
}
/**
* 数据分组
* 根据 category 分组
*/
private void groups(){
for (Data data : datalist) {
String category = data.getCategory();
// 如果 postMap 中不包含该 category,则新建一个 List
// 否则,获取已存在的 List
List<Data> categoryList = postMap.computeIfAbsent(category, k -> new ArrayList<>());
// 将当前的 Data 对象添加到 category 对应的 List 中
categoryList.add(data);
// 初始化适配器
expandableListAdapter = new ParentAdapter(MainActivity.this, postMap);
// 设置 ExpandableListView 的适配器
expandableListView.setAdapter(expandableListAdapter);
}
}
}
activity_main.xml(主布局)
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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=".MainActivity">
<ExpandableListView
android:id="@+id/expandableListView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:divider="@android:color/transparent"
android:dividerHeight="0dp"
android:groupIndicator="@null"/>
</androidx.constraintlayout.widget.ConstraintLayout>
ParentAdapter.java
public class ParentAdapter implements ExpandableListAdapter {
private Context context;
private HashMap<String, List<Data>> postMap;
private final List<String> categories;
public ParentAdapter(Context context, HashMap<String, List<Data>> postMap) {
this.context = context;
this.postMap = postMap;
this.categories = new ArrayList<>(postMap.keySet());
}
@Override
public void registerDataSetObserver(DataSetObserver observer) {
}
@Override
public void unregisterDataSetObserver(DataSetObserver observer) {
}
@Override
public int getGroupCount() {
return categories.size();
}
@Override
public int getChildrenCount(int groupPosition) {
return 1;
}
@Override
public Object getGroup(int groupPosition) {
return categories.get(groupPosition);
}
@Override
public Object getChild(int groupPosition, int childPosition) {
return categories.get(groupPosition);
}
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
ParentHolder parentHolder = null;
String category = (String) getGroup(groupPosition);
if(convertView == null) {
LayoutInflater userInflater = (LayoutInflater) context.getSystemService(context.LAYOUT_INFLATER_SERVICE);
convertView = userInflater.inflate(R.layout.item_parent, null);
convertView.setHorizontalScrollBarEnabled(true);
parentHolder = new ParentHolder();
convertView.setTag(parentHolder);
} else {
parentHolder = (ParentHolder) convertView.getTag();
}
parentHolder.category = (TextView) convertView.findViewById(R.id.category);
parentHolder.category.setText(category);
return convertView;
}
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
String category=categories.get(groupPosition);
//String category = (String) getGroup(groupPosition);
ChildHolder childHolder = null;
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.item_group_child, parent, false);
childHolder = new ChildHolder();
convertView.setTag(childHolder);
}
else {
childHolder = (ChildHolder) convertView.getTag();
}
childHolder.horizontalListView = (RecyclerView) convertView.findViewById(R.id.recycreyView);
// 设置 RecyclerView 的布局管理器和适配器
LinearLayoutManager layoutManager = new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false);
childHolder.horizontalListView.setLayoutManager(layoutManager);
ChildAdapter horizontalListAdapter = new ChildAdapter(context, postMap.get(category));
childHolder.horizontalListView.setAdapter(horizontalListAdapter);
return convertView;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return false;
}
@Override
public boolean areAllItemsEnabled() {
return false;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public void onGroupExpanded(int groupPosition) {
}
@Override
public void onGroupCollapsed(int groupPosition) {
}
@Override
public long getCombinedChildId(long groupId, long childId) {
return 0;
}
@Override
public long getCombinedGroupId(long groupId) {
return 0;
}
private static class ChildHolder {
static RecyclerView horizontalListView;
}
private static class ParentHolder {
TextView category;
}
}
item_parent.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="wrap_content"
android:gravity="center_vertical">
<TextView
android:id="@+id/category"
android:textSize="18dp"
android:text="Text"
android:layout_weight="1"
android:gravity="center|left"
android:paddingLeft="10dp"
android:background="#FA7298"
android:textColor="@color/white"
android:layout_width="wrap_content"
android:layout_height="50dp" />
</LinearLayout>
item_group_child.xml(这个就是子视图的容器,也可以使用ListView,使用RecyclerView可以实现更多布局展现方例如线性、瀑布流等)
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/recycreyView"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"/>
ChildAdapter.java(子视图RecyclerView的适配器)
public class ChildAdapter extends RecyclerView.Adapter<ChildAdapter.ViewHolder> {
private Context context;
private List<Data> mobiles;
public ChildAdapter(Context context, List<Data> mobiles) {
this.context = context;
this.mobiles = mobiles;
}
static class ViewHolder extends RecyclerView.ViewHolder{
TextView content;
public ViewHolder(View view){
super(view);
content=view.findViewById(R.id.content);
}
}
@NonNull
@Override
public ChildAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_child, parent, false);
context = parent.getContext();
return new ChildAdapter.ViewHolder(view);
}
@SuppressLint("RecyclerView")
@Override
public void onBindViewHolder(@NonNull ChildAdapter.ViewHolder holder, int position) {
Data post = mobiles.get(position);
holder.content.setText(post.getContent());
holder.content.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context, "你点击了"+post.getContent(), Toast.LENGTH_SHORT).show();
}
});
}
@Override
public int getItemCount() {
return mobiles.size();
}
}
item_child.xml(子视图)
<?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="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/content"
android:textSize="18dp"
android:text="Text"
android:layout_weight="1"
android:gravity="center|left"
android:paddingLeft="10dp"
android:textColor="@color/black"
android:layout_width="match_parent"
android:layout_height="50dp"
android:clickable="true"
android:background="?android:attr/selectableItemBackground"/>
</LinearLayout>
最后别忘了Data.java
public class Data {
private String category;//标签分组
private String content;//内容
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Data(String category, String content) {
this.category = category;
this.content = content;
}
}
好了代码基本就是以上这些了,可能给人第一眼感觉有点复杂,其实当你在使用ExpandableListView的时候,你就会发现其实很简单就像一个RecclerView再嵌套一个RecyclerView(我也不知道这个比喻恰当不😂😂),而且分组列表在Android开发应用还是比较多的,我就不一一举例了。
有任何问题欢迎在评论区指正。