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

Android中实现工厂→部门-科室→产线→设备四级ExpandableListView

四级层级列表实现方案(Plant→Dept-Section→Line→Device)

方案选型:优先推荐RecyclerView嵌套方案

原生ExpandableListView仅支持两级,嵌套ExpandableListView会存在滚动冲突、复用性差的问题,子项嵌入RecyclerView的方案性能和扩展性更优:

  • 利用RecyclerView的视图复用机制,滚动流畅度更高
  • 多类型Item支持轻松适配不同层级的UI
  • 后续扩展更多层级时改动更小

如果必须基于ExpandableListView改造,也可以通过将中间层级伪装成"Group"实现,但逻辑复杂度更高,不推荐。


第一步:设计嵌套式数据结构

用实体类嵌套对应层级关系,每个父节点包含下一级子节点列表:

// 工厂实体
public class Plant {
    public String id;
    public String name;
    public boolean isExpanded; // 记录展开状态
    public List<DeptSection> deptSections;
}

// 部门-科室实体
public class DeptSection {
    public String id;
    public String name;
    public boolean isExpanded;
    public List<Line> lines;
}

// 产线实体
public class Line {
    public String id;
    public String name;
    public boolean isExpanded;
    public List<Device> devices;
}

// 设备实体
public class Device {
    public String id;
    public String name;
}

第二步:实现多类型嵌套RecyclerView Adapter

核心思路是:

  1. 定义四种Item类型:PLANT、DEPT_SECTION、LINE、DEVICE
  2. 每个类型对应独立的ViewHolder,其中DeptSection和Line的ViewHolder中嵌入RecyclerView
  3. 维护各层级的展开/折叠状态,点击时切换状态并刷新对应Item

关键代码示例:

public class HierarchyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private static final int TYPE_PLANT = 0;
    private static final int TYPE_DEPT_SECTION = 1;
    private static final int TYPE_LINE = 2;
    private static final int TYPE_DEVICE = 3;

    private List<Object> mDataList; // 混合存储所有层级的实体

    // 构造方法:将Plant列表转成扁平的混合列表(初始仅显示Plant)
    public HierarchyAdapter(List<Plant> plantList) {
        mDataList = new ArrayList<>();
        for (Plant plant : plantList) {
            mDataList.add(plant);
            // 默认折叠,不添加子项
        }
    }

    @Override
    public int getItemViewType(int position) {
        Object item = mDataList.get(position);
        if (item instanceof Plant) return TYPE_PLANT;
        if (item instanceof DeptSection) return TYPE_DEPT_SECTION;
        if (item instanceof Line) return TYPE_LINE;
        if (item instanceof Device) return TYPE_DEVICE;
        return -1;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        switch (viewType) {
            case TYPE_PLANT:
                View plantView = inflater.inflate(R.layout.item_plant, parent, false);
                return new PlantViewHolder(plantView);
            case TYPE_DEPT_SECTION:
                View deptView = inflater.inflate(R.layout.item_dept_section, parent, false);
                return new DeptSectionViewHolder(deptView);
            case TYPE_LINE:
                View lineView = inflater.inflate(R.layout.item_line, parent, false);
                return new LineViewHolder(lineView);
            case TYPE_DEVICE:
                View deviceView = inflater.inflate(R.layout.item_device, parent, false);
                return new DeviceViewHolder(deviceView);
            default:
                throw new IllegalArgumentException("Unknown view type");
        }
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        Object item = mDataList.get(position);
        switch (getItemViewType(position)) {
            case TYPE_PLANT:
                Plant plant = (Plant) item;
                PlantViewHolder plantHolder = (PlantViewHolder) holder;
                plantHolder.tvName.setText(plant.name);
                plantHolder.ivArrow.setRotation(plant.isExpanded ? 90 : 0);
                // 点击切换展开/折叠
                plantHolder.itemView.setOnClickListener(v -> {
                    plant.isExpanded = !plant.isExpanded;
                    plantHolder.ivArrow.setRotation(plant.isExpanded ? 90 : 0);
                    toggleChildItems(position, plant.deptSections, plant.isExpanded);
                });
                break;
            case TYPE_DEPT_SECTION:
                DeptSection dept = (DeptSection) item;
                DeptSectionViewHolder deptHolder = (DeptSectionViewHolder) holder;
                deptHolder.tvName.setText(dept.name);
                deptHolder.ivArrow.setRotation(dept.isExpanded ? 90 : 0);
                // 配置子RecyclerView(产线列表)
                LineAdapter lineAdapter = new LineAdapter(dept.lines, dept.isExpanded);
                deptHolder.rvLines.setAdapter(lineAdapter);
                deptHolder.rvLines.setLayoutManager(new LinearLayoutManager(holder.itemView.getContext()));
                // 点击切换展开/折叠
                deptHolder.itemView.setOnClickListener(v -> {
                    dept.isExpanded = !dept.isExpanded;
                    deptHolder.ivArrow.setRotation(dept.isExpanded ? 90 : 0);
                    lineAdapter.toggleExpand(dept.isExpanded);
                });
                break;
            // Line和Device的绑定逻辑类似,LineViewHolder中嵌入Device的RecyclerView
            // ...
        }
    }

    // 核心方法:添加/移除子项到扁平列表
    private <T> void toggleChildItems(int parentPosition, List<T> childList, boolean isExpanded) {
        if (isExpanded) {
            // 展开:在父Item后插入所有子项
            mDataList.addAll(parentPosition + 1, childList);
            notifyItemRangeInserted(parentPosition + 1, childList.size());
        } else {
            // 折叠:移除父Item后的所有子项(直到下一个同级父Item)
            int removeStart = parentPosition + 1;
            int removeCount = 0;
            while (removeStart + removeCount < mDataList.size()) {
                Object nextItem = mDataList.get(removeStart + removeCount);
                // 判断是否是当前父项的子类型(比如Plant的子项是DeptSection)
                if (nextItem instanceof DeptSection) {
                    removeCount++;
                } else {
                    break;
                }
            }
            mDataList.subList(removeStart, removeStart + removeCount).clear();
            notifyItemRangeRemoved(removeStart, removeCount);
        }
    }

    // 各层级ViewHolder定义
    static class PlantViewHolder extends RecyclerView.ViewHolder {
        TextView tvName;
        ImageView ivArrow;
        public PlantViewHolder(View itemView) {
            super(itemView);
            tvName = itemView.findViewById(R.id.tv_plant_name);
            ivArrow = itemView.findViewById(R.id.iv_arrow);
        }
    }

    static class DeptSectionViewHolder extends RecyclerView.ViewHolder {
        TextView tvName;
        ImageView ivArrow;
        RecyclerView rvLines;
        public DeptSectionViewHolder(View itemView) {
            super(itemView);
            tvName = itemView.findViewById(R.id.tv_dept_name);
            ivArrow = itemView.findViewById(R.id.iv_arrow);
            rvLines = itemView.findViewById(R.id.rv_lines);
        }
    }

    // LineViewHolder和DeviceViewHolder类似
    // ...
}

第三步:处理嵌套滚动冲突

在嵌套RecyclerView的布局中,给内层RecyclerView添加属性:

android:nestedScrollingEnabled="false"

这样内层RecyclerView不会拦截滚动事件,交由外层统一处理,避免滚动卡顿。


备选方案:基于ExpandableListView改造

如果必须用ExpandableListView,可以将中间层级(DeptSection、Line)都当作"Group",Device作为最终Child,需要自定义Adapter并手动维护层级状态:

  1. 用一个扁平列表存储所有Group(Plant、DeptSection、Line),并记录每个Group的层级深度
  2. 重写getGroupCount返回所有Group的数量
  3. 重写getChildCount(int groupPosition):如果当前Group是Line,返回对应的Device数量;否则返回0(因为下一级是Group而非Child)
  4. 重写getGroupView和getChildView,根据层级深度渲染不同UI
  5. 自己维护每个Group的展开状态,点击时刷新对应位置

这种方式逻辑繁琐,容易出现状态混乱,仅适合必须兼容旧代码的场景。

内容的提问来源于stack exchange,提问作者Karan Satpute

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 01:24:52