当前位置:

首页 > 编程开发 > Android中RecyclerView实现多级列表展示效果

Android中RecyclerView实现多级列表展示效果

Android Studio macOS版
Android Studio macOS版

Android Studio 是 Google 面向安卓应用开发提供的官方集成开发环境,基于 IntelliJ IDEA 构建。它整合智能代码编辑、Gradle 构建、界面预览、设备模拟、性能分析、调试测试及 APK 分析等工具。

立即下载
Mac 2026-10-13
数据库 编程开发 服务器管理 集成开发环境

使用扁平列表维护节点数据,动态插入/移除子节点并调整缩进,模拟多级列表展开效果。该方法避免嵌套Adapter的递归性能开销,列表滑动更流畅,代码逻辑简洁清晰,可维护性与复用性显著提升。

本文目录

    多级列表在Android开发中是个常见需求,但用RecyclerView实现时,很多人会下意识地想着用嵌套Adapter或者多层次的布局来搞。其实还有一个更清爽的思路——只维护一个扁平的列表,通过动态插入子节点数据和调整缩进来模拟层级展开效果。这样既避免了嵌套Adapter带来的性能开销,代码逻辑也清晰得多。下面就把这个实现方案完整拆一遍。

    1. 项目依赖与布局准备

    首先在build.gradle里把RecyclerView依赖加上,没什么好说的:

    dependencies {
        implementation 'com.android.support:recyclerview-v7:+'
    }

    主布局文件activity_main.xml就是一个普通的RecyclerView,无需额外处理:

    
    
        
    

    每个列表项的布局文件item_multi_level.xml里需要一个箭头图标和一个文本,注意箭头初始是隐藏的——只有有子节点的节点才显示:

    
    
        
        
    

    2. 数据模型:TreeNode

    每个节点需要记录自己的id、名称、层级、展开状态,以及父子关系。为了支持无限层级,使用parent和children双向关联。关键方法有两个:addChild自动设置孩子的层级和父节点;hasChildren判断是否为叶子节点。

    package com.example.recycler;
    import ja va.util.ArrayList;
    import ja va.util.List;
    
    public class TreeNode {
        private String id;
        private String name;
        private int level; // 层级,0开始
        private boolean isExpanded; // 是否展开
        private TreeNode parent; // 父节点
        private List children; // 子节点
        private Object data; // 其他数据
    
        public TreeNode(String id, String name,int level) {
            this.id = id;
            this.name = name;
            this.children = new ArrayList<>();
            this.isExpanded = false;
            this.level = level;
        }
    
        // Getters and Setters
        public String getId() { return id; }
        public void setId(String id) { this.id = id; }
        public String getName() { return name; }
        public void setName(String name) { this.name = name; }
        public int getLevel() { return level; }
        public void setLevel(int level) { this.level = level; }
        public boolean isExpanded() { return isExpanded; }
        public void setExpanded(boolean expanded) { isExpanded = expanded; }
        public TreeNode getParent() { return parent; }
        public void setParent(TreeNode parent) { this.parent = parent; }
        public List getChildren() { return children; }
        public void setChildren(List children) { this.children = children; }
        public Object getData() { return data; }
        public void setData(Object data) { this.data = data; }
    
        // 添加子节点
        public void addChild(TreeNode child) {
            child.setParent(this);
            child.setLevel(this.level + 1);
            children.add(child);
        }
    
        // 判断是否有子节点
        public boolean hasChildren() {
            return children != null && !children.isEmpty();
        }
    }

    3. 适配器:核心逻辑就在这里

    适配器维护两个列表:allNodes是完整的树结构数据,displayNodes则是根据展开状态动态计算出的扁平列表。每次点击节点时,如果它有子节点,就切换展开/收起状态,然后重新计算displayNodes,并通过notifyItemRangeInserted或notifyItemRangeRemoved来刷新UI。

    缩进的处理很简单:在onBindViewHolder中根据节点的level设置左填充,每级缩进50dp。箭头图标在有子节点时显示,并根据展开状态切换背景图片(你替换为自己的展开/收起图标即可)。

    package com.example.recycler;
    import android.support.annotation.NonNull;
    import android.support.v7.widget.RecyclerView;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.ImageView;
    import android.widget.TextView;
    import ja va.util.ArrayList;
    import ja va.util.List;
    
    public class MultiLevelAdapter extends RecyclerView.Adapter {
        private List displayNodes; // 实际显示的数据
        private List allNodes; // 所有数据
        private OnItemClickListener listener;
    
        public MultiLevelAdapter(List data) {
            this.allNodes = data;
            this.displayNodes = getDisplayNodes(data);
        }
    
        // 获取要显示的节点(展开的节点)
        private List getDisplayNodes(List nodes) {
            List result = new ArrayList<>();
            for (TreeNode node : nodes) {
                result.add(node);
                if (node.isExpanded() && node.hasChildren()) {
                    result.addAll(getDisplayNodes(node.getChildren()));
                }
            }
            return result;
        }
    
        @NonNull
        @Override
        public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.item_multi_level, parent, false);
            return new ViewHolder(view);
        }
    
        @Override
        public void onBindViewHolder(@NonNull final ViewHolder holder, int position) {
            TreeNode node = displayNodes.get(position);
            // 设置缩进
            int paddingLeft = node.getLevel() * 50; // 每级缩进50dp
            holder.itemView.setPadding(paddingLeft, 0, 0, 0);
    
            // 设置文本
            holder.textView.setText(node.getName());
    
            // 设置箭头图标
            if (node.hasChildren()) {
                holder.arrowIcon.setVisibility(View.VISIBLE);
            } else {
                holder.arrowIcon.setVisibility(View.GONE);
            }
    
            if (node.isExpanded())
                //展开图片设置
                holder.arrowIcon.setBackground(你的图片)
            else
                //收起图片设置
                holder.arrowIcon.setBackground(你的图片)
    
            // 点击事件
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int adapterPosition = holder.getAdapterPosition();
                    if (adapterPosition != RecyclerView.NO_POSITION) {
                        TreeNode clickedNode = displayNodes.get(adapterPosition);
                        if (clickedNode.hasChildren()) {
                            // 有子节点:切换展开/收起状态
                            int removeCount = 0;
                            if (clickedNode.isExpanded()) {
                                removeCount = countAllChildren(clickedNode);
                                clearAllChildExpand(clickedNode);
                            }
                            clickedNode.setExpanded(!clickedNode.isExpanded());
                            refreshDisplayNodes();
                            //int a = 收起图片
                            if (clickedNode.isExpanded()) {
                                a = 展开图片
                                notifyItemRangeInserted(adapterPosition + 1, clickedNode.getChildren().size());
                            } else if (!clickedNode.isExpanded() && removeCount != 0) {
                                notifyItemRangeRemoved(adapterPosition + 1, removeCount);
                            }
                            holder.arrowIcon.setBackground(a);
                        } else {
                            // 没有子节点:触发点击事件
                            if (listener != null) {
                                listener.onItemClick(clickedNode);
                            }
                        }
                    }
                }
            });
    
            // 长按事件(可选)
            holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    if (listener != null) {
                        return listener.onItemLongClick(displayNodes.get(holder.getAdapterPosition()));
                    }
                    return false;
                }
            });
        }
    
        // 计算节点的所有子节点数量(包括子节点的子节点)
        private int countAllChildren(TreeNode node) {
            int count = 0;
            if (node.hasChildren() && node.isExpanded()) {
                for (TreeNode child : node.getChildren()) {
                    count++;
                    if (child.isExpanded()) {
                        count += countAllChildren(child);
                    }
                }
            }
            return count;
        }
    
        private void clearAllChildExpand(TreeNode node) {
            if (node.hasChildren()) {
                for (TreeNode child : node.getChildren()) {
                    child.setExpanded(false);
                    clearAllChildExpand(child);
                }
            }
        }
    
        // 刷新显示的数据
        private void refreshDisplayNodes() {
            displayNodes = getDisplayNodes(allNodes);
        }
    
        @Override
        public int getItemCount() {
            return displayNodes.size();
        }
    
        // 设置数据
        public void setData(List data) {
            this.allNodes = data;
            refreshDisplayNodes();
            notifyDataSetChanged();
        }
    
        // 展开/收起所有节点
        public void expandAll() {
            setExpandedForAll(allNodes, true);
            refreshDisplayNodes();
            notifyDataSetChanged();
        }
    
        public void collapseAll() {
            setExpandedForAll(allNodes, false);
            refreshDisplayNodes();
            notifyDataSetChanged();
        }
    
        private void setExpandedForAll(List nodes, boolean expanded) {
            for (TreeNode node : nodes) {
                node.setExpanded(expanded);
                if (node.hasChildren()) {
                    setExpandedForAll(node.getChildren(), expanded);
                }
            }
        }
    
        // ViewHolder
        static class ViewHolder extends RecyclerView.ViewHolder {
            TextView textView;
            ImageView arrowIcon;
    
            public ViewHolder(@NonNull View itemView) {
                super(itemView);
                textView = itemView.findViewById(R.id.tv_item_name);
                arrowIcon = itemView.findViewById(R.id.iv_arrow);
            }
        }
    
        // 点击监听接口
        public interface OnItemClickListener {
            void onItemClick(TreeNode node);
            boolean onItemLongClick(TreeNode node);
        }
    
        public void setOnItemClickListener(OnItemClickListener listener) {
            this.listener = listener;
        }
    }

    4. 在Activity中使用

    测试数据里构造了1~3级,并且在一个分支里演示了4级,足够体现无限层级的潜力。注意层级从0开始,根节点level=0,每添加一个子节点level自动+1。点击叶子节点会弹Toast并打印日志,你可以在这里接入自己的业务逻辑。长按事件则用来弹出一个选项对话框(示例中只是一个占位方法)。

    package com.example.recycler;
    import android.app.Activity;
    import android.os.Bundle;
    import android.support.v7.widget.LinearLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.util.Log;
    import android.widget.Toast;
    import ja va.util.ArrayList;
    import ja va.util.List;
    
    public class MainActivity extends Activity {
        private RecyclerView recyclerView;
        private MultiLevelAdapter adapter;
    
        @Override
        protected void onCreate(Bundle sa vedInstanceState) {
            super.onCreate(sa vedInstanceState);
            setContentView(R.layout.activity_main);
    
            recyclerView = findViewById(R.id.recyclerView);
            recyclerView.setLayoutManager(new LinearLayoutManager(this));
    
            // 创建测试数据
            List data = createTestData();
            adapter = new MultiLevelAdapter(data);
            recyclerView.setAdapter(adapter);
    
            // 设置点击监听
            adapter.setOnItemClickListener(new MultiLevelAdapter.OnItemClickListener() {
                @Override
                public void onItemClick(TreeNode node) {
                    // 处理没有子节点的点击事件
                    Toast.makeText(MainActivity.this,
                            "点击了: " + node.getName(), Toast.LENGTH_SHORT).show();
                    handleLeafClick(node);
                }
    
                @Override
                public boolean onItemLongClick(TreeNode node) {
                    // 长按事件
                    showOptionsDialog(node);
                    return true;
                }
            });
        }
    
        // 创建测试数据(支持无限层级)
        private List createTestData() {
            List rootNodes = new ArrayList<>();
            // 第1级
            for (int i = 1; i <= 3; i++) {
                TreeNode level1 = new TreeNode("1-" + i, "一级节点 " + i, 0);
                level1.setLevel(0);
                // 第2级
                for (int j = 1; j <= 2; j++) {
                    TreeNode level2 = new TreeNode("2-" + i + "-" + j, "二级节点 " + i + "-" + j, level1.getLevel());
                    level1.addChild(level2);
                    // 第3级
                    for (int k = 1; k <= (i == 1 ? 3 : 0); k++) {
                        TreeNode level3 = new TreeNode("3-" + i + "-" + j + "-" + k,
                                "三级节点 " + i + "-" + j + "-" + k, level2.getLevel());
                        level2.addChild(level3);
                        // 第4级(演示无限层级)
                        if (i == 1 && j == 1) {
                            for (int l = 1; l <= 2; l++) {
                                TreeNode level4 = new TreeNode("4-" + i + "-" + j + "-" + k + "-" + l,
                                        "四级节点 " + i + "-" + j + "-" + k + "-" + l, level3.getLevel());
                                level3.addChild(level4);
                            }
                        }
                    }
                }
                rootNodes.add(level1);
            }
            return rootNodes;
        }
    
        // 处理叶子节点点击
        private void handleLeafClick(TreeNode node) {
            Log.d("MultiLevel", "点击了叶子节点: " + node.getName());
        }
    }

    这个方案的核心优势在于数据结构与显示列表分离,所有节点在后台以树形组织,显示时只拉取当前展开的节点。展开/收起时局部刷新的性能也远优于全数据集刷新。代码可以直接拿去用,记得把占位的箭头图片替换成自己的展开/收起图标即可。

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发 Android
    相关文章 更多
    一文看懂灵活计算器手机端和电脑端的使用区别
    一文看懂灵活计算器手机端和电脑端的使用区别

    想知道灵活计算器在手机和电脑上怎么用最高效?本文详解两端在输入节奏、布局整理及账号同步上的核心区别,助你避免数据丢失,建立顺畅的跨设备记账习惯。

    Android安装包无法打开?灵活计算器APK安装排查指南
    Android安装包无法打开?灵活计算器APK安装排查指南

    遇到灵活计算器APK无法打开或解析错误?本文详解Android侧载安装的排查逻辑,涵盖来源验证、权限配置、空间清理及旧版本冲突处理,帮助用户安全解决安装问题并正确恢复会员权益。

    XMind iOS版与安卓版功能有什么区别?
    XMind iOS版与安卓版功能有什么区别?

    XMind iOS版和安卓版都能完成完整的思维导图编辑,但在文件保存、导出格式及系统分享流程上存在差异。本文详解两端在实际使用中的不同点,帮助跨平台用户优化工作流。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。

    即将离开本站
    您即将前往第三方网站,请确认是否继续?