You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于Firebase Database实现Section ListView的数据获取?

如何用Firebase Database结合Section List Adapter实现分组ListView的数据检索

嘿,我来分享一下我在项目里实现这个需求的实操步骤,跟着走应该就能搞定啦~

一、先规划Firebase数据结构

分组列表的核心是数据要自带分组标识,比如做联系人按首字母分组,我的Firebase数据结构是这样的:

{
  "contacts": {
    "A": {
      "alice": {
        "name": "Alice Smith",
        "email": "alice@example.com"
      },
      "andrew": {
        "name": "Andrew Johnson",
        "email": "andrew@example.com"
      }
    },
    "B": {
      "bob": {
        "name": "Bob Williams",
        "email": "bob@example.com"
      }
    }
  }
}

顶级节点是分组的key(比如首字母),下面嵌套该组的具体数据,这样读取时能直接对应到分组。

二、创建数据模型类

先定义两个实体类:一个存单条数据,一个存分组信息:

1. 单条数据模型(比如联系人)

public class Contact {
    private String name;
    private String email;

    // 必须要有无参构造!Firebase反序列化需要它
    public Contact() {}

    public Contact(String name, String email) {
        this.name = name;
        this.email = email;
    }

    // Getter & Setter 方法
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
}

2. 分组模型

public class Section {
    private String title; // 分组标题(比如"A")
    private List<Contact> items; // 该组下的所有数据

    public Section(String title, List<Contact> items) {
        this.title = title;
        this.items = items;
    }

    public String getTitle() { return title; }
    public List<Contact> getItems() { return items; }
}

三、从Firebase读取并整理分组数据

接下来要把Firebase里的原始数据转换成List<Section>格式,适配后续的Adapter:

// 获取Firebase数据库引用
DatabaseReference dbRef = FirebaseDatabase.getInstance().getReference("contacts");

// 添加值事件监听器,读取数据
dbRef.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(@NonNull DataSnapshot snapshot) {
        List<Section> sectionList = new ArrayList<>();

        // 遍历每个分组节点(A、B、C...)
        for (DataSnapshot sectionSnapshot : snapshot.getChildren()) {
            String sectionTitle = sectionSnapshot.getKey();
            List<Contact> contactList = new ArrayList<>();

            // 遍历分组下的每条数据
            for (DataSnapshot contactSnapshot : sectionSnapshot.getChildren()) {
                Contact contact = contactSnapshot.getValue(Contact.class);
                if (contact != null) {
                    contactList.add(contact);
                }
            }

            // 将分组和数据加入列表
            sectionList.add(new Section(sectionTitle, contactList));
        }

        // 拿到整理好的数据后,绑定到Adapter
        setupSectionListView(sectionList);
    }

    @Override
    public void onCancelled(@NonNull DatabaseError error) {
        // 处理读取失败的情况
        Log.e("FirebaseError", "读取数据失败: " + error.getMessage());
    }
});

四、实现Section List Adapter

这里我用ExpandableListView配合自定义适配器来实现分组列表,代码如下:

public class SectionListAdapter extends BaseExpandableListAdapter {
    private Context context;
    private List<Section> sectionList;
    private List<Section> originalSectionList; // 保存原始数据,用于搜索过滤

    public SectionListAdapter(Context context, List<Section> sectionList) {
        this.context = context;
        this.sectionList = sectionList;
        this.originalSectionList = new ArrayList<>(sectionList); // 备份原始数据
    }

    // --- 基础Adapter方法 ---
    @Override
    public int getGroupCount() {
        return sectionList.size();
    }

    @Override
    public int getChildrenCount(int groupPosition) {
        return sectionList.get(groupPosition).getItems().size();
    }

    @Override
    public Object getGroup(int groupPosition) {
        return sectionList.get(groupPosition);
    }

    @Override
    public Object getChild(int groupPosition, int childPosition) {
        return sectionList.get(groupPosition).getItems().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) {
        Section section = (Section) getGroup(groupPosition);
        if (convertView == null) {
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.section_title_layout, parent, false);
        }

        TextView titleTv = convertView.findViewById(R.id.section_title);
        titleTv.setText(section.getTitle());
        return convertView;
    }

    // --- 渲染分组内的子项 ---
    @Override
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
        Contact contact = (Contact) getChild(groupPosition, childPosition);
        if (convertView == null) {
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.contact_item_layout, parent, false);
        }

        TextView nameTv = convertView.findViewById(R.id.contact_name);
        TextView emailTv = convertView.findViewById(R.id.contact_email);
        nameTv.setText(contact.getName());
        emailTv.setText(contact.getEmail());
        return convertView;
    }

    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return true; // 允许子项被点击
    }

    // --- 搜索过滤方法 ---
    public void filter(String query) {
        sectionList.clear();
        if (query.isEmpty()) {
            sectionList.addAll(originalSectionList);
        } else {
            String lowerQuery = query.toLowerCase();
            for (Section section : originalSectionList) {
                List<Contact> filteredContacts = new ArrayList<>();
                // 过滤匹配的子项
                for (Contact contact : section.getItems()) {
                    if (contact.getName().toLowerCase().contains(lowerQuery)
                            || contact.getEmail().toLowerCase().contains(lowerQuery)) {
                        filteredContacts.add(contact);
                    }
                }
                // 只有分组下有匹配项时,才保留该分组
                if (!filteredContacts.isEmpty()) {
                    sectionList.add(new Section(section.getTitle(), filteredContacts));
                }
            }
        }
        notifyDataSetChanged();
    }
}

对应的布局文件

  • section_title_layout.xml(分组标题布局):
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/section_title"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="12dp"
    android:background="#f0f0f0"
    android:textStyle="bold"
    android:textSize="16sp"/>
  • contact_item_layout.xml(子项布局):
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="16dp">

    <TextView
        android:id="@+id/contact_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="16sp"/>

    <TextView
        android:id="@+id/contact_email"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="14sp"
        android:textColor="#666666"
        android:layout_marginTop="4dp"/>
</LinearLayout>

五、绑定适配器并实现搜索功能

1. 绑定适配器到ListView

private void setupSectionListView(List<Section> sectionList) {
    ExpandableListView expandableListView = findViewById(R.id.expandable_list_view);
    SectionListAdapter adapter = new SectionListAdapter(this, sectionList);
    expandableListView.setAdapter(adapter);

    // 可选:默认展开所有分组
    for (int i = 0; i < adapter.getGroupCount(); i++) {
        expandableListView.expandGroup(i);
    }
}

2. 添加搜索监听

在Activity里给搜索框添加监听,调用适配器的过滤方法:

SearchView searchView = findViewById(R.id.search_view);
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
    @Override
    public boolean onQueryTextSubmit(String query) {
        adapter.filter(query);
        return false;
    }

    @Override
    public boolean onQueryTextChange(String newText) {
        adapter.filter(newText);
        return false;
    }
});

几个注意事项

  • Firebase数据库规则:开发阶段可以暂时设置为{ "rules": { ".read": true, ".write": true } },上线后一定要改成安全的规则(比如只允许授权用户读取)。
  • 数据量大时,建议用addChildEventListener代替addValueEventListener,监听增量变化,避免重复加载全部数据。
  • 如果需要匹配分组标题,在过滤逻辑里加上section.getTitle().toLowerCase().contains(lowerQuery)的判断即可。

内容的提问来源于stack exchange,提问作者Sddam Awawdh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:55:05