Android Studio Kotlin多布局RecyclerView数据缺失问题求助
RecyclerView多布局异常排查与修复
问题现象
实现了支持三种记录类型的RecyclerView多布局展示:
- 仅包含1型(SHIP_ROW)或同时包含1型、2型(STATEROOM_ROW)记录时,所有数据正常显示:

- 当数据中存在3型(STATEROOM_PROPERTIES_ROW)记录时,该记录之后的所有1型、2型记录均无法展示,无报错信息:

问题代码
class ShipsAdapter (private val stateroomInfo: ArrayList<StateroomAdapterData>, private val listener: OnItemClickListener): RecyclerView.Adapter<RecyclerView.ViewHolder>() { private var rowType = 0 override fun getItemViewType(position: Int): Int { rowType = when (stateroomInfo[position].adapterRecordType) { Constants.SHIP_ROW -> Constants.SHIP_ROW Constants.STATEROOM_ROW -> Constants.STATEROOM_ROW else -> Constants.STATEROOM_PROPERTIES_ROW } return rowType } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder = when (rowType){ Constants.SHIP_ROW -> { val view = ListShipsRecyclerItemRowBinding.inflate(LayoutInflater.from(parent.context), parent, false) ListShipsViewHolder(view) } Constants.STATEROOM_ROW -> { val view = ListStateroomsRecyclerItemRowBinding.inflate(LayoutInflater.from(parent.context), parent, false) ListStateroomsViewHolder(view) } else -> { val view = ListStateroomPropertiesRecyclerItemRowBinding.inflate(LayoutInflater.from(parent.context), parent, false) ListStateroomPropertiesViewHolder(view) } } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { when (rowType) { Constants.SHIP_ROW -> (holder as ListShipsViewHolder).bind(position) Constants.STATEROOM_ROW -> (holder as ListStateroomsViewHolder).bind(position) Constants.STATEROOM_PROPERTIES_ROW -> (holder as ListStateroomPropertiesViewHolder).bind(position) } } override fun getItemCount(): Int { return stateroomInfo.size } inner class ListShipsViewHolder(shipsRow: ListShipsRecyclerItemRowBinding): RecyclerView.ViewHolder(shipsRow.root), View.OnClickListener { private val tvShip = shipsRow.tvShip private var ivEditShip = shipsRow.ivEditShip private var ivDeleteShip = shipsRow.ivDeleteShip private var ivStateroom = shipsRow.ivStateroom init { ivEditShip.setOnClickListener(this) ivDeleteShip.setOnClickListener(this) ivStateroom.setOnClickListener(this) } override fun onClick(v: View?) { if (adapterPosition != RecyclerView.NO_POSITION) if (v != null) { when (v.id) { R.id.ivEditShip -> listener.onItemClicked(adapterPosition, Constants.EDIT_SHIP_CLICKED) R.id.ivDeleteShip -> listener.onItemClicked(adapterPosition, Constants.DELETE_SHIP_CLICKED) R.id.ivStateroom -> listener.onItemClicked(adapterPosition, Constants.STATEROOM_CLICKED) } } } fun bind(position: Int) { val shipData = stateroomInfo[position] tvShip.text = shipData.ship } } inner class ListStateroomsViewHolder(stateroomRow: ListStateroomsRecyclerItemRowBinding): RecyclerView.ViewHolder(stateroomRow.root), View.OnClickListener { private val tvStateroom = stateroomRow.tvStateroom private var ivEditStateroom = stateroomRow.ivEditStateroom private var ivDeleteStateroom = stateroomRow.ivDeleteStateroom init { ivEditStateroom.setOnClickListener(this) ivDeleteStateroom.setOnClickListener(this) } override fun onClick(v: View?) { if (adapterPosition != RecyclerView.NO_POSITION) if (v != null) { when (v.id) { R.id.ivEditStateroom -> listener.onItemClicked(adapterPosition, Constants.EDIT_STATEROOM_CLICKED) R.id.ivDeleteStateroom -> listener.onItemClicked(adapterPosition, Constants.DELETE_STATEROOM_CLICKED) } } } fun bind(position: Int) { val stateroomData = stateroomInfo[position] tvStateroom.text = stateroomData.stateroom } } inner class ListStateroomPropertiesViewHolder(stateroomPropertiesRow: ListStateroomPropertiesRecyclerItemRowBinding): RecyclerView.ViewHolder(stateroomPropertiesRow.root), View.OnClickListener { private val tvBedrooms = stateroomPropertiesRow.tvBedrooms private val tvBaths = stateroomPropertiesRow.tvBathrooms private val tvConnecting = stateroomPropertiesRow.tvIsConnecting private val ivEditStateroomProperties = stateroomPropertiesRow.ivEditStateroomProperties init { ivEditStateroomProperties.setOnClickListener(this) } fun bind(position: Int) { val stateroomPropertiesData = stateroomInfo[position] tvBedrooms.text = stateroomPropertiesData.numBedrooms tvBaths.text = stateroomPropertiesData.numBaths tvConnecting.text = stateroomPropertiesData.connectingSuite } override fun onClick(v: View?) { if (adapterPosition != RecyclerView.NO_POSITION) if (v != null) { listener.onItemClicked(adapterPosition, Constants.EDIT_STATEROOM_PROPERTIES_CLICKED) } } } // 用于和Fragment的点击事件通信 interface OnItemClickListener { fun onItemClicked(position: Int, clickType: Int) } }
问题原因
核心问题在于全局变量rowType被错误复用:
getItemViewType会为每个Item计算类型并赋值给rowType,但RecyclerView的ViewHolder创建和绑定是异步批量执行的- 当3型记录出现后,
rowType被固定为3型,后续onCreateViewHolder和onBindViewHolder直接使用这个全局值,而非当前Item的真实类型 - 导致后续原本是1型/2型的Item被错误创建为3型ViewHolder,绑定数据时类型不匹配,最终无法正常显示内容
修复方案
移除全局rowType变量,直接使用方法传入的参数或当前Item的真实类型:
1. 修改getItemViewType
直接返回计算结果,不再赋值给全局变量:
override fun getItemViewType(position: Int): Int { return when (stateroomInfo[position].adapterRecordType) { Constants.SHIP_ROW -> Constants.SHIP_ROW Constants.STATEROOM_ROW -> Constants.STATEROOM_ROW else -> Constants.STATEROOM_PROPERTIES_ROW } }
2. 修改onCreateViewHolder
使用方法参数viewType判断类型:
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder = when (viewType){ Constants.SHIP_ROW -> { val view = ListShipsRecyclerItemRowBinding.inflate(LayoutInflater.from(parent.context), parent, false) ListShipsViewHolder(view) } Constants.STATEROOM_ROW -> { val view = ListStateroomsRecyclerItemRowBinding.inflate(LayoutInflater.from(parent.context), parent, false) ListStateroomsViewHolder(view) } else -> { val view = ListStateroomPropertiesRecyclerItemRowBinding.inflate(LayoutInflater.from(parent.context), parent, false) ListStateroomPropertiesViewHolder(view) } }
3. 修改onBindViewHolder
通过当前Item的真实类型或ViewHolder的itemViewType判断:
// 方式一:直接获取当前Item的类型 override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { val itemType = stateroomInfo[position].adapterRecordType when (itemType) { Constants.SHIP_ROW -> (holder as ListShipsViewHolder).bind(position) Constants.STATEROOM_ROW -> (holder as ListStateroomsViewHolder).bind(position) else -> (holder as ListStateroomPropertiesViewHolder).bind(position) } } // 方式二:使用ViewHolder的itemViewType override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { when (holder.itemViewType) { Constants.SHIP_ROW -> (holder as ListShipsViewHolder).bind(position) Constants.STATEROOM_ROW -> (holder as ListStateroomsViewHolder).bind(position) Constants.STATEROOM_PROPERTIES_ROW -> (holder as ListStateroomPropertiesViewHolder).bind(position) } }
说明
修改后,每个Item的ViewHolder创建和数据绑定都会基于自身的真实类型,不会被之前Item的类型干扰,3型记录之后的1型、2型记录即可正常展示。
内容的提问来源于stack exchange,提问作者WGP
相关产品推荐
相关产品推荐

