如何在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
相关产品推荐
相关产品推荐

