Android Fragment无参构造问题:ViewPager2重复显示数据
问题:ViewPager2中Fragment使用newInstance传递数据导致所有页面显示相同内容
我实现了ViewPager2的SectionPager2适配器并结合TabLayout使用,遇到以下问题:使用companion object的newInstance方法创建HomeDetailTempFragment实例时,ViewPager内的多个Fragment会显示相同数据;改用带参构造创建时显示正常,但这种方式可能在部分设备上触发“无法实例化”的致命错误。
1. SectionPager2适配器代码
class SectionPager2(fragment: Fragment): FragmentStateAdapter(fragment) { private val fragmentList = mutableListOf<Fragment>() override fun getItemCount() = fragmentList.size override fun createFragment(position: Int) = fragmentList[position] fun addFragment(position: Int, fragment: Fragment) { fragmentList.add(position, fragment) } }
2. ViewPager2结合TabLayout的使用代码
val sectionPagerAdapter = SectionPager2(this) for (item in gradeSectionMajorOrdered) { var title = "" fragment = HomeDetailTempFragment.newInstance(thisFragment, item.majors!!, item.gradeSectionId) gradeSectionIds.add(item.gradeSectionId) for (i in viewModel.gradeSections.value?.result!!) { if (i.id == item.gradeSectionId) title = i.name } majorItems.add(item.majors) gradeSectionId = item.gradeSectionId sectionPagerAdapter.addFragment(gradeSectionMajorOrdered.indexOf(item), fragment) fragmentTitleList.add(gradeSectionMajorOrdered.indexOf(item), title) }
3. 有问题的HomeDetailTempFragment实现(newInstance方式)
@SuppressLint("ValidFragment") class HomeDetailTempFragment : BaseFragment(), MajorRecyclerViewAdapter.IMajorRV { companion object { private lateinit var listener: OnClick private lateinit var items: List<MajorGrades> private var gradeSectionId = 0 fun newInstance( listener: HomeDetailFragment, items: List<MajorGrades>, gradeSectionId: Int, ) = HomeDetailTempFragment().also { this.listener = listener this.gradeSectionId = gradeSectionId this.items = items } } private lateinit var mContext : Context private lateinit var binding: FragmentHomeDetailTempBinding override fun onCreateView(inflater : LayoutInflater, container : ViewGroup?, savedInstanceState : Bundle?) : View { binding = FragmentHomeDetailTempBinding.inflate(layoutInflater) return binding.root } override fun onViewCreated(view : View, savedInstanceState : Bundle?) { super.onViewCreated(view, savedInstanceState) mContext = requireContext() binding.rvGradeMajor.adapter = MajorRecyclerViewAdapter(context = mContext, majors = items, gradeSectionId = gradeSectionId, listener = this) } override fun onGradeMajorClick(majorItem : MajorGrades, gradeItem : MajorGrades) { listener.onGradeMajorClick(gradeSectionId, majorItem, gradeItem) } }
4. 显示正常但有风险的实现(带参构造方式)
@SuppressLint("ValidFragment") class HomeDetailTempFragment(private val listener : OnClick, private val items : List<MajorGrades>, private val gradeSectionId : Int) : BaseFragment(), MajorRecyclerViewAdapter.IMajorRV { private lateinit var mContext : Context private lateinit var binding: FragmentHomeDetailTempBinding override fun onCreateView(inflater : LayoutInflater, container : ViewGroup?, savedInstanceState : Bundle?) : View { binding = FragmentHomeDetailTempBinding.inflate(layoutInflater) return binding.root } override fun onViewCreated(view : View, savedInstanceState : Bundle?) { super.onViewCreated(view, savedInstanceState) mContext = requireContext() binding.rvGradeMajor.adapter = MajorRecyclerViewAdapter(context = mContext, majors = items, gradeSectionId = gradeSectionId, listener = this) } override fun onGradeMajorClick(majorItem : MajorGrades, gradeItem : MajorGrades) { listener.onGradeMajorClick(gradeSectionId, majorItem, gradeItem) } }
问题根源
newInstance方法使用了companion object的静态私有变量存储listener、items、gradeSectionId,这些变量属于类而非实例。循环创建Fragment时,每次调用newInstance都会覆盖静态变量的值,导致所有Fragment最终引用的都是最后一次赋值的参数,因此显示相同数据。
而带参构造的方式虽然能正常显示,但Fragment的系统重建逻辑(如屏幕旋转、内存回收恢复)会调用无参构造函数,带参构造的实例此时无法被系统实例化,从而抛出“无法实例化”的致命异常。
正确解决方案
Fragment传递参数的标准做法是用Bundle存储参数,通过arguments传递,同时保留无参构造函数。修改后的HomeDetailTempFragment如下:
class HomeDetailTempFragment : BaseFragment(), MajorRecyclerViewAdapter.IMajorRV { // 定义参数Key companion object { private const val KEY_LISTENER = "key_listener" private const val KEY_ITEMS = "key_items" private const val KEY_GRADE_SECTION_ID = "key_grade_section_id" fun newInstance( listener: OnClick, items: List<MajorGrades>, gradeSectionId: Int ): HomeDetailTempFragment { val fragment = HomeDetailTempFragment() val args = Bundle().apply { // 注意:OnClick接口需实现Serializable,或改用ViewModel传递回调 putSerializable(KEY_LISTENER, listener as? Serializable) // 假设MajorGrades实现了Parcelable,用ArrayList包装后存入 putParcelableArrayList(KEY_ITEMS, ArrayList(items)) putInt(KEY_GRADE_SECTION_ID, gradeSectionId) } fragment.arguments = args return fragment } } private lateinit var listener: OnClick private lateinit var items: List<MajorGrades> private var gradeSectionId = 0 private lateinit var mContext: Context private lateinit var binding: FragmentHomeDetailTempBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 从arguments中取出参数 arguments?.let { args -> listener = args.getSerializable(KEY_LISTENER) as OnClick items = args.getParcelableArrayList<MajorGrades>(KEY_ITEMS) ?: emptyList() gradeSectionId = args.getInt(KEY_GRADE_SECTION_ID) } } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { binding = FragmentHomeDetailTempBinding.inflate(layoutInflater) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) mContext = requireContext() binding.rvGradeMajor.adapter = MajorRecyclerViewAdapter( context = mContext, majors = items, gradeSectionId = gradeSectionId, listener = this ) } override fun onGradeMajorClick(majorItem: MajorGrades, gradeItem: MajorGrades) { listener.onGradeMajorClick(gradeSectionId, majorItem, gradeItem) } }
注意事项
- 确保
MajorGrades类实现Parcelable或Serializable接口,推荐用Parcelable以获得更高效率。 - 若
OnClick接口持有Activity/Fragment引用,不建议序列化,改用ViewModel或LiveData传递回调可避免内存泄漏风险。 - 移除
@SuppressLint("ValidFragment")注解,标准无参构造无需抑制警告。 - SectionPager2适配器无需修改,保持原有逻辑即可。
内容的提问来源于stack exchange,提问作者jafar_aml
相关产品推荐
相关产品推荐

