You need to enable JavaScript to run this app. 如何实现支持折叠/展开切换展示条目的Android RecyclerView
优惠活动
大模型
产品
解决方案
定价
更多

如何实现支持折叠/展开切换展示条目的Android RecyclerView

RecyclerView 折叠功能实现方案

核心思路是将折叠逻辑内聚到Adapter内部,不拆分原始数据源,用局部刷新代替全量刷新,实现更简洁高效。


具体实现步骤

1. 内部变量与基础配置

Adapter内部直接持有全量数据源,新增展开状态标记、预览条数常量,同时将底部操作按钮作为单独的Item类型处理,和普通数据条目逻辑隔离:

class FoldableListAdapter(
    // 仅初始化时传入一次全量数据即可,后续无需切换数据源
    private val fullData: List<YourDataBean>
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
    // Item类型区分
    private val TYPE_DATA = 0
    private val TYPE_FOOTER = 1
    // 预览状态展示的条目数
    private val PREVIEW_COUNT = 2
    // 展开状态标记,默认收起
    private var isExpanded = false

2. 重写条目计数与类型判断

根据展开状态动态计算实际展示的条目数量,数据源总条数小于等于2时可以直接隐藏底部按钮:

override fun getItemCount(): Int {
        // 数据总量小于等于预览数,直接展示全部数据,不需要底部按钮
        if (fullData.size <= PREVIEW_COUNT) return fullData.size
        // 否则计算展示的数据条数+1个底部按钮
        val showDataCount = if (isExpanded) fullData.size else PREVIEW_COUNT
        return showDataCount + 1
    }

    override fun getItemViewType(position: Int): Int {
        return if (position == itemCount - 1 && fullData.size > PREVIEW_COUNT) {
            TYPE_FOOTER
        } else {
            TYPE_DATA
        }
    }

3. 绑定逻辑与状态切换

条目绑定正常处理,底部按钮点击时切换状态,用局部刷新API完成列表更新,自带条目增删动画:

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        when (getItemViewType(position)) {
            TYPE_DATA -> {
                (holder as DataViewHolder).bind(fullData[position])
            }
            TYPE_FOOTER -> {
                val footerHolder = holder as FooterViewHolder
                footerHolder.operateBtn.text = if (isExpanded) "隐藏" else "查看全部"
                footerHolder.operateBtn.setOnClickListener {
                    toggleExpand()
                }
            }
        }
    }

    private fun toggleExpand() {
        val oldShowCount = if (isExpanded) fullData.size else PREVIEW_COUNT
        isExpanded = !isExpanded
        val newShowCount = if (isExpanded) fullData.size else PREVIEW_COUNT
        val diffCount = newShowCount - oldShowCount
        // 局部刷新代替全量notifyDataSetChanged
        if (diffCount > 0) {
            notifyItemRangeInserted(PREVIEW_COUNT, diffCount)
        } else {
            notifyItemRangeRemoved(PREVIEW_COUNT, -diffCount)
        }
        // 刷新底部按钮文字
        notifyItemChanged(itemCount - 1)
    }

    // 两种ViewHolder的定义、创建逻辑此处省略,按常规RecyclerView写法实现即可
}

方案优势

  • 逻辑完全内聚在Adapter内部,外部无需处理状态、无需切换传入的数据源,仅需初始化时传入全量列表即可
  • 用notifyItemRangeInserted/notifyItemRangeRemoved做局部刷新,性能比全量刷新高,还自带系统默认的条目增删动画
  • 没有多余的嵌套逻辑,完全匹配仅需两种展示状态的需求,后续要修改预览条数仅需修改PREVIEW_COUNT常量即可
  • 自动适配数据量小于等于预览数的场景,不会多余展示底部操作按钮

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.08 02:57:03