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

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)
    }
}

注意事项

  1. 确保MajorGrades类实现Parcelable或Serializable接口,推荐用Parcelable以获得更高效率。
  2. 若OnClick接口持有Activity/Fragment引用,不建议序列化,改用ViewModel或LiveData传递回调可避免内存泄漏风险。
  3. 移除@SuppressLint("ValidFragment")注解,标准无参构造无需抑制警告。
  4. SectionPager2适配器无需修改,保持原有逻辑即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 08:27:26