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

Android横向RecyclerView两行不等宽布局问题求助

横向GridLayoutManager RecyclerView动态设置Item宽度失效问题解决

问题背景

在Android中使用带GridLayoutManager的横向RecyclerView实现布局时,通过设备宽度动态设置Item宽度,仅第一个位置的宽度生效,onBindViewHolder中能正确获取到位置值(0、1、2等),但后续Item宽度不生效。

需求与当前效果

需求设计
需求设计图

当前输出
当前效果图

现有代码

RecyclerView声明代码

val layoutManager: RecyclerView.LayoutManager =
            GridLayoutManager(requireContext(), 2, GridLayoutManager.HORIZONTAL, false)
binding.rvStoreTypesHome.setLayoutManager(layoutManager)
binding.rvStoreTypesHome.adapter = StoreTypesAdapter(requireContext())

Adapter代码

class StoreTypesAdapter(val context: Context) :
    RecyclerView.Adapter<StoreTypesAdapter.ViewHolder>() {
var width = 0

init {
    width = context.getDeviceWidth()
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
    val binding =
        StoreTypesAdapterBinding.inflate(LayoutInflater.from(parent.context), parent, false)
    return ViewHolder(binding)
}

@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: ViewHolder, position: Int) {

    val layoutParams = holder.binding.conSTAdapter.layoutParams
    layoutParams.width = if (holder.adapterPosition % 2 == 0) {
        (width / 2.5).toInt()
    } else {
        (width / 3.5).toInt()
    }
    layoutParams.height = ConstraintLayout.LayoutParams.WRAP_CONTENT
    holder.binding.conSTAdapter.layoutParams = layoutParams
    holder.binding.tvStoreNameSTAdapter.text = "Store Name $position"
}

override fun getItemCount(): Int {
    return 10
}

inner class ViewHolder(var binding: StoreTypesAdapterBinding) :
    RecyclerView.ViewHolder(binding.root)

}

问题原因

GridLayoutManager在横向网格模式下,会基于设置的列数(此处为2)统一计算并约束Item的宽度,onBindViewHolder中修改的LayoutParams会被LayoutManager的测量布局逻辑覆盖,仅第一个Item因初始渲染时机问题暂时生效,后续Item复用或滚动时会被强制重置宽度。

解决方案

方案1:自定义GridLayoutManager重写测量逻辑

通过自定义GridLayoutManager,在测量阶段动态设置每个Item的宽度,避免被默认逻辑覆盖:

class CustomGridLayoutManager(context: Context, spanCount: Int, orientation: Int, reverseLayout: Boolean) :
    GridLayoutManager(context, spanCount, orientation, reverseLayout) {

    private val deviceWidth = context.resources.displayMetrics.widthPixels

    override fun measureChildWithMargins(child: View, widthUsed: Int, heightUsed: Int) {
        val position = getPosition(child)
        val layoutParams = child.layoutParams as RecyclerView.LayoutParams
        // 根据位置设置对应宽度
        layoutParams.width = if (position % 2 == 0) {
            (deviceWidth / 2.5).toInt()
        } else {
            (deviceWidth / 3.5).toInt()
        }
        child.layoutParams = layoutParams
        super.measureChildWithMargins(child, widthUsed, heightUsed)
    }
}

替换原有LayoutManager:

val layoutManager = CustomGridLayoutManager(requireContext(), 2, GridLayoutManager.HORIZONTAL, false)
binding.rvStoreTypesHome.layoutManager = layoutManager
binding.rvStoreTypesHome.adapter = StoreTypesAdapter(requireContext())

方案2:改用LinearLayoutManager(更简单)

由于需求是横向交替宽度的Item,直接使用横向LinearLayoutManager,它不会强制统一Item宽度,原有Adapter逻辑即可生效:

  1. 修改RecyclerView的LayoutManager:
val layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)
binding.rvStoreTypesHome.layoutManager = layoutManager
binding.rvStoreTypesHome.adapter = StoreTypesAdapter(requireContext())
  1. 调整Adapter的onBindViewHolder,直接使用参数中的position(比adapterPosition更可靠):
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    val layoutParams = holder.binding.conSTAdapter.layoutParams as ViewGroup.LayoutParams
    layoutParams.width = if (position % 2 == 0) {
        (context.resources.displayMetrics.widthPixels / 2.5).toInt()
    } else {
        (context.resources.displayMetrics.widthPixels / 3.5).toInt()
    }
    holder.binding.conSTAdapter.layoutParams = layoutParams
    holder.binding.tvStoreNameSTAdapter.text = "Store Name $position"
}

注意事项

  • 优先使用onBindViewHolder参数中的position,而非holder.adapterPosition,避免Item复用导致的位置偏差
  • 获取设备宽度建议使用context.resources.displayMetrics.widthPixels,替代自定义的getDeviceWidth()方法,兼容性更好

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 09:00:02