两个RecyclerView复用同一Adapter出现布局加载异常求助
问题分析与解决方案
问题描述
在TabLayout中使用两个Fragment复用同一个RecyclerView Adapter时,出现布局加载异常:第一个Fragment本该显示的内容被跳过,第二个Fragment的内容错误显示在第一个Fragment上,第二个Fragment为空。调试器报错:E/RecyclerView: No adapter attached; skipping layout。
核心问题原因
- 错误获取View实例:两个Fragment的
initView()方法中,使用requireActivity().findViewById(R.id.loanRecRcv)获取RecyclerView,这会拿到Activity中的同一个RecyclerView实例,导致两个Fragment操作的是同一个控件,内容串位。 - Fragment内部列表未同步:调用
loanRecRcvAdapter.setData()后,Fragment自身的list变量没有更新,点击事件中会出现数据不匹配的问题。 - Adapter泛型缺失:Adapter定义中
private var list: List = emptyList()没有指定泛型类型,存在类型安全隐患。
修复后的代码
第一个Fragment(OpenLoansFragment)
private lateinit var swipeRefreshLayout: SwipeRefreshLayout private lateinit var loanRecRcv: RecyclerView private var list: ArrayList<LoanRecordModel> = ArrayList() private lateinit var loanRecRcvAdapter: LoanRecRcvAdapter // 延迟初始化,避免提前绑定空列表 override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { _binding = FragmentOpenLoansBinding.inflate(inflater, container, false) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) initView() // 初始化Adapter并绑定数据 val dataList = listOf( LoanRecordModel("", "Petty Cash Loan", "12/05/2022", "Open") ) list.clear() list.addAll(dataList) loanRecRcvAdapter = LoanRecRcvAdapter(list, this) // 配置RecyclerView loanRecRcv.apply { layoutManager = LinearLayoutManager(activity) adapter = loanRecRcvAdapter } } override fun OnClick(position: Int) { Toast.makeText(activity, list[position].loan_type, Toast.LENGTH_SHORT).show() } private fun initView() { // 使用ViewBinding获取当前Fragment布局内的控件,而非Activity的 swipeRefreshLayout = binding.swipeRefresh loanRecRcv = binding.loanRecRcv }
第二个Fragment(ClosedLoansFragment)
private lateinit var swipeRefreshLayout: SwipeRefreshLayout private lateinit var loanRecRcv: RecyclerView private var list: ArrayList<LoanRecordModel> = ArrayList() private lateinit var loanRecRcvAdapter: LoanRecRcvAdapter override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { _binding = FragmentClosedLoansBinding.inflate(inflater, container, false) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) initView() // 初始化数据和Adapter val dataList = listOf( LoanRecordModel("", "Regular Loan", "12/05/2022", "Closed"), LoanRecordModel("", "Petty Cash Loan", "05/04/2022", "Closed") ) list.clear() list.addAll(dataList) loanRecRcvAdapter = LoanRecRcvAdapter(list, this) // 配置RecyclerView loanRecRcv.apply { layoutManager = LinearLayoutManager(activity) adapter = loanRecRcvAdapter } } override fun OnClick(position: Int) { Toast.makeText(activity, list[position].loan_type, Toast.LENGTH_SHORT).show() } private fun initView() { // 使用ViewBinding获取当前Fragment的控件 swipeRefreshLayout = binding.swipeRefresh loanRecRcv = binding.loanRecRcv }
修复后的Adapter
class LoanRecRcvAdapter( private var list: List<LoanRecordModel> = emptyList(), // 补充泛型 val listener: MyOnClickListener ) : RecyclerView.Adapter<LoanRecRcvAdapter.MyViewHolder>() { inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val typeTv = itemView.findViewById<TextView>(R.id.loanType) val dateTv = itemView.findViewById<TextView>(R.id.dueDate) init { itemView.setOnClickListener { val position = adapterPosition if (position != RecyclerView.NO_POSITION) { // 防止无效位置点击 listener.OnClick(position) } } } } fun setData(data: List<LoanRecordModel>) { list = data.toList() notifyDataSetChanged() } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.record_layout, parent, false) return MyViewHolder(view) } override fun onBindViewHolder(holder: MyViewHolder, position: Int) { val item = list[position] holder.apply { typeTv.text = item.loan_type dateTv.text = item.due_date } } override fun getItemCount(): Int { return list.size } interface MyOnClickListener { fun OnClick(position: Int) } }
关键修复点说明
- 控件获取方式:改用ViewBinding的
binding.xxx获取当前Fragment布局内的控件,确保每个Fragment操作自己的RecyclerView实例。 - Adapter初始化时机:延迟到
onViewCreated中初始化Adapter,确保数据准备完成后再绑定到RecyclerView,避免空Adapter警告。 - 数据同步:更新Adapter数据时,同时同步Fragment自身的
list变量,保证点击事件数据一致。 - 泛型与安全优化:补充Adapter列表的泛型类型,添加点击位置有效性判断,提升代码安全性。
内容的提问来源于stack exchange,提问作者Nigel Manimtim
相关产品推荐
相关产品推荐

