ViewModelProvider.Factory 总是 return 一个视图模型

ViewModelProvider.Factory always return one viewmodel

我有一个 TabLayout,包含 SectionsPagerAdapter 的三个 Fragment(由同一实例创建)。在片段中,我尝试使用 ViewModelProvider.Factory 创建独立的视图模型,但是,我发现所有片段总是使用相同的数据更新内容。

I have debugged and found it always return the same viewmodel even with difference BillType, and something's weird that when enterence into activity, the Factory.create is only invoked once.

// Log
D/BillType: OUTCOME
D/Factory crate BillType: OUTCOME
D/ViewModel Init BillType: OUTCOME
D/viewModel Bill:BillType: OUTCOME
D/BillType: INCOME
D/viewModel Bill:BillType: OUTCOME
D/BillType: TRANSFER
D/viewModel Bill:BillType: OUTCOME

我不知道哪里错了,同样的代码之前运行正确。

class BillViewModel(billType: BillType): ViewModel() {
    val bill: MutableLiveData<Bill> = MutableLiveData()

    init {
        Log.d("ViewModel Init BillType", billType.toString())
        bill.value = Bill.QBill().apply {
            type = billType
        }
    }

    class NewBillViewModelFactory(val billType: BillType): ViewModelProvider.Factory {
        override fun <T : ViewModel?> create(modelClass: Class<T>): T {
            Log.d("Factory crate BillType", billType.toString())
            return modelClass.getConstructor(BillType::class.java)
                .newInstance(billType)
        }
    }
}
enum class BillType(val type: Int) {
    OUTCOME(0),
    INCOME(1),
    TRANSFER(2);
}
class NewBillFragment: BaseFragment() {
    ...
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        billType = BillType.values()[arguments?.getInt(BILLTYPE, 0) ?: 0]
        Log.d("BillType", billType.toString())
        viewModel = ViewModelProvider(requireActivity(), NewBillViewModel.NewBillViewModelFactory(billType))[NewBillViewModel::class.java]
        Log.d("viewModel Bill:BillType", viewModel.bill.value?.type.toString())
        _binding = FragmentBillNewBinding.inflate(layoutInflater, container, false)
        with(binding) {
            data = viewModel
            lifecycleOwner = activity
        ... ui ...
        return binding.root
    }

    companion object {
        private const val BILLTYPE = "billtype"

        @JvmStatic
        fun newInstance(billType: Int): NewBillFragment {
            return NewBillFragment().apply {
                arguments = Bundle().apply {
                    putInt(BILLTYPE, billType)
                }
            }
        }
    }
}
class SectionsPagerAdapter(private val context: Context, fm: FragmentManager)
    : FragmentPagerAdapter(fm) {

    override fun getItem(position: Int): Fragment = BillFragment.newInstance(position)

    override fun getPageTitle(position: Int): CharSequence = context.resources.getString(TAB_TITLES[position])

    override fun getCount(): Int = 3
}

因为您正在与 requireActivity() 创建共享 ViewModel 。所以它将 return ViewModel 参考 Activity 而不是 Fragment.

如果你想保持 ViewModel 片段范围那么你应该将 Fragment 作为 ViewModelStoreOwner 传递。

        viewModel = ViewModelProvider(this, NewBillViewModel.NewBillViewModelFactory(billType))[NewBillViewModel::class.java]