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

如何在RecyclerView中实现网格项宽高平滑扩展至全屏?

实现网格RecyclerView项的全屏平滑扩展

我完全懂你的需求——那些围绕Expandable RecyclerView的方案确实只局限于调整item的高度,用来展开详情内容或者折叠ActionBar这类场景,根本满足不了你要的「3列网格项点击后平滑扩展成占满屏幕的1×1项」的效果。下面给你一套可行的实现思路和代码示例:

核心思路拆解

要实现这个效果,关键在于动态修改单个item的布局跨度+平滑的尺寸过渡动画,具体分这几步:

1. 自定义GridLayoutManager,支持单个item跨全列

默认的GridLayoutManager没法单独修改某个item的跨度,所以我们需要继承它,添加选中状态的管理,让选中的item占据所有列(3列的话就返回spanCount=3):

class ExpandableGridLayoutManager(
    context: Context,
    private val spanCount: Int
) : GridLayoutManager(context, spanCount) {

    private var expandedPosition = -1 // 记录当前展开的item位置,-1表示无展开项

    fun toggleExpandedPosition(position: Int) {
        val previousExpanded = expandedPosition
        if (expandedPosition == position) {
            expandedPosition = -1 // 点击已展开的项,取消展开
        } else {
            expandedPosition = position
        }
        // 通知RecyclerView更新这两个位置的item布局
        notifyItemChanged(previousExpanded)
        notifyItemChanged(position)
    }

    override fun getSpanSize(position: Int): Int {
        // 展开项占满所有列,其他项占1列
        return if (position == expandedPosition) spanCount else 1
    }
}

2. 添加平滑的尺寸过渡动画

光改布局跨度会让item瞬间变大,不够流畅。我们需要在ViewHolder的点击事件里,给itemView添加属性动画,让尺寸变化平滑过渡:

class GridItemAdapter(private val dataList: List<String>) : RecyclerView.Adapter<GridItemAdapter.ItemViewHolder>() {

    private lateinit var layoutManager: ExpandableGridLayoutManager

    fun setLayoutManager(manager: ExpandableGridLayoutManager) {
        this.layoutManager = manager
    }

    inner class ItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val itemContent: TextView = itemView.findViewById(R.id.tv_item_content)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.item_grid, parent, false)
        return ItemViewHolder(view)
    }

    override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
        holder.itemContent.text = dataList[position]

        holder.itemView.setOnClickListener {
            layoutManager.toggleExpandedPosition(position)
            
            // 获取屏幕可用宽度(减去RecyclerView的padding)
            val screenWidth = holder.itemView.context.resources.displayMetrics.widthPixels
            val targetWidth = screenWidth - (layoutManager.paddingLeft + layoutManager.paddingRight)
            // 目标高度可以和宽度一致,或者根据需求设置
            val targetHeight = targetWidth

            // 执行平滑尺寸动画
            if (position == layoutManager.expandedPosition) {
                // 展开动画:从原尺寸过渡到全屏尺寸
                holder.itemView.animate()
                    .width(targetWidth)
                    .height(targetHeight)
                    .setDuration(300)
                    .setInterpolator(AccelerateDecelerateInterpolator())
                    .start()
            } else {
                // 收起动画:从全屏尺寸过渡到原网格项尺寸
                val originalWidth = (screenWidth - (layoutManager.paddingLeft + layoutManager.paddingRight)) / layoutManager.spanCount
                val originalHeight = originalWidth // 假设是正方形项
                holder.itemView.animate()
                    .width(originalWidth)
                    .height(originalHeight)
                    .setDuration(300)
                    .setInterpolator(AccelerateDecelerateInterpolator())
                    .start()
            }

            // 可选:让展开的item居中显示
            layoutManager.scrollToPositionWithOffset(position, 0)
        }
    }

    override fun getItemCount(): Int = dataList.size
}

3. 初始化RecyclerView时关联自定义LayoutManager

在Activity/Fragment里初始化RecyclerView:

val recyclerView = findViewById<RecyclerView>(R.id.rv_grid)
val spanCount = 3
val layoutManager = ExpandableGridLayoutManager(this, spanCount)
recyclerView.layoutManager = layoutManager

val adapter = GridItemAdapter(List(9) { "Item ${it+1}" })
adapter.setLayoutManager(layoutManager)
recyclerView.adapter = adapter
// 可选:禁用RecyclerView的滑动,避免展开时滑动干扰
// recyclerView.isNestedScrollingEnabled = false

注意事项

  • 如果你的网格项不是正方形,需要调整动画里的originalHeight计算逻辑,确保收起时回到正确的尺寸。
  • 处理屏幕旋转时,要通过onSaveInstanceState保存expandedPosition,恢复时重新设置给LayoutManager。
  • 动画期间可以暂时禁用RecyclerView的滑动,避免用户操作导致布局混乱。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 08:40:24