LiveData无法观察到变化

2024-05-06

我正在更新一个ViewModel 中 DialogFragment 的 LiveData 值,但无法获取Fragment中的值。

视图模型:

class OtpViewModel(private val otpUseCase: OtpUseCase, analyticsModel: IAnalyticsModel) : BaseViewModel(analyticsModel) {
    override val globalNavModel = GlobalNavModel(titleId = R.string.otp_contact_title, hasGlobalNavBar = false)

    private val _contactListLiveData = MutableLiveData<List<Contact>>()
    val contactListLiveData: LiveData<List<Contact>>
        get() = _contactListLiveData

    private lateinit var cachedContactList: LiveData<List<Contact>>
    private val contactListObserver = Observer<List<Contact>> {
        _contactListLiveData.value = it
    }



    private lateinit var cachedResendOtpResponse: LiveData<LogonModel>
    private val resendOTPResponseObserver = Observer<LogonModel> {
        _resendOTPResponse.value = it
    }

    private var _resendOTPResponse = MutableLiveData<LogonModel>()
    val resendOTPResponseLiveData: LiveData<LogonModel>
        get() = _resendOTPResponse

    var userSelectedIndex : Int = 0 //First otp contact selected by default

    val selectedContact : LiveData<Contact>
        get() = MutableLiveData(contactListLiveData.value?.get(userSelectedIndex))

    override fun onCleared() {
        if (::cachedContactList.isInitialized) {
            cachedContactList.removeObserver(contactListObserver)
        }

        if (::cachedOtpResponse.isInitialized) {
            cachedOtpResponse.removeObserver(otpResponseObserver)
        }

        super.onCleared()
    }

    fun updateIndex(pos: Int){
        userSelectedIndex = pos
    }

    fun onChangeDeliveryMethod() {
        navigate(
            OtpVerificationHelpCodeSentBottomSheetFragmentDirections
                .actionOtpContactVerificationBottomSheetToOtpChooseContactFragment()
        )
    }

    fun onClickContactCancel() {
        navigateBackTo(R.id.logonFragment, true)
    }

    fun retrieveContactList() {
        cachedContactList = otpUseCase.fetchContactList()
        cachedContactList.observeForever(contactListObserver)
    }



    fun resendOTP(contactId : String){
        navigateBack()
        cachedResendOtpResponse = otpUseCase.resendOTP(contactId)
        cachedResendOtpResponse.observeForever(resendOTPResponseObserver)

    }
}

基本视图模型:

abstract class BaseViewModel(val analyticsModel: IAnalyticsModel) : ViewModel() {
    protected val _navigationCommands: SingleLiveEvent<NavigationCommand> = SingleLiveEvent()
    val navigationCommands: LiveData<NavigationCommand> = _navigationCommands

    abstract val globalNavModel: GlobalNavModel


    /**
     * Posts a navigation event to the navigationsCommands LiveData observable for retrieval by the view
     */
    fun navigate(directions: NavDirections) {
        _navigationCommands.postValue(NavigationCommand.ToDirections(directions))
    }

    fun navigate(destinationId: Int) {
        _navigationCommands.postValue(NavigationCommand.ToDestinationId(destinationId))
    }

    fun navigateBack() {
        _navigationCommands.postValue(NavigationCommand.Back)
    }

    fun navigateBackTo(destinationId: Int, isInclusive: Boolean) {
        _navigationCommands.postValue(NavigationCommand.BackTo(destinationId, isInclusive))
    }

    open fun init() {
        // DEFAULT IMPLEMENTATION - override to initialize your view model
    }


    /**
     * Called from base fragment when the view has been created.
     */
    fun onViewCreated() {
        analyticsModel.onNewState(getAnalyticsPathCrumb())
    }

    /**
     * gets the Path for the current page to be used for the trackstate call
     *
     * Override this method if you need to modify the path
     *
     * the page id for the track state call will be calculated in the following manner
     * 1) analyticsPageId
     * 2) titleId
     * 3) the page title string
     */
    protected fun getAnalyticsPathCrumb() : AnalyticsBreadCrumb {

        return analyticsBreadCrumb {
            pathElements {
                if (globalNavModel.analyticsPageId != null) {
                    waPath {
                        path = PathElement(globalNavModel.analyticsPageId as Int)
                    }
                } else if (globalNavModel.titleId != null) {
                    waPath {
                        path = PathElement(globalNavModel.titleId as Int)
                    }
                } else {
                    waPath {
                        path = PathElement(globalNavModel.title ?: "")
                    }
                }
            }
        }
    }
}

对话框片段:

class OtpVerificationHelpCodeSentBottomSheetFragment : BaseBottomSheetDialogFragment(){

    private lateinit var rootView: View
    lateinit var binding: BottomSheetFragmentOtpVerificationHelpCodeSentBinding

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {

        viewModel = getViewModel<OtpViewModel>()

        binding = DataBindingUtil.inflate(inflater, R.layout.bottom_sheet_fragment_otp_verification_help_code_sent, container, false)

        rootView = binding.root

        return rootView
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)


        val otpViewModel = (viewModel as OtpViewModel)
        binding.viewmodel = otpViewModel

        otpViewModel.resendOTPResponseLiveData.observe(viewLifecycleOwner, Observer {

            it?.let { resendOtpResponse ->
                if(resendOtpResponse.statusCode.equals("000")){
                    //valid status code
                    requireActivity().toastMessageOtp(getString(R.string.otp_code_verification_sent))
                }else{
                    //show the error model
                    //it?.errorModel?.let { it1 -> handleDiasNetworkError(it1) }
                }
            }

        })
    }
}

我正在打电话重新发送OTP(联系人ID:字符串)来自 xml 文件的 viewmodel 的方法对话框片段:

 <TextView
            android:id="@+id/verification_help_code_sent_resend_code"
            style="@style/TruTextView.SubText2.BottomActions"
            android:layout_height="@dimen/spaceXl"
            android:gravity="center_vertical"
            android:text="@string/verification_help_resend_code"
            android:onClick="@{() -> viewmodel.resendOTP(Integer.toString(viewmodel.userSelectedIndex))}"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/top_guideline" />

现在,每当我尝试从片段调用 resendOTPResponseLiveData 时,它都不会被调用:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        Log.d("OtpVerify" , "OnViewCreatedCalled")
        viewModel.onViewCreated()
        val otpViewModel = (viewModel as OtpViewModel)

        binding.lifecycleOwner = this
        binding.viewmodel = otpViewModel
        binding.toAuthenticated = OtpVerifyFragmentDirections.actionOtpVerifyFragmentToAuthenticatedActivity()
        binding.toVerificationBtmSheet = OtpVerifyFragmentDirections.actionOtpVerifyFragmentToOtpContactVerificationCodeSentBottomSheet()


        otpViewModel.resendOTPResponseLiveData.observe(viewLifecycleOwner, Observer {
            if(it?.statusCode.equals("000")){
                //valid status code
                requireActivity().toastMessageOtp(getString(R.string.otp_code_verification_sent))
            }else{
                //show the error model
                it?.errorModel?.let { it1 -> handleDiasNetworkError(it1) }
            }
        })

    }

那么我在这里做错了什么。

EDIT

基本上我需要在对话框片段中单击侦听器(重新发送按钮单击),并且需要在片段中读取它。所以我使用了SharedViewModel的概念。

所以我在 ViewModel 中进行了必要的更改:

private val selected = MutableLiveData<LogonModel>()

 fun select(logonModel: LogonModel) {
        selected.value = logonModel
    }

    fun getSelected(): LiveData<LogonModel> {
        return selected
    }

在对话框片段中:

 otpViewModel.resendOTPResponseLiveData.observe(viewLifecycleOwner, Observer{

           otpViewModel.select(it);

        })

在我想读取值的片段中:

otpViewModel.getSelected().observe(viewLifecycleOwner, Observer {

            Log.d("OtpVerify" , "ResendCalled")
            // Update the UI.
            if(it?.statusCode.equals("000")){
                //valid status code
                requireActivity().toastMessageOtp(getString(R.string.otp_code_verification_sent))
            }else{
                //show the error model
                it?.errorModel?.let { it1 -> handleDiasNetworkError(it1) }
            }
        })

但它仍然不起作用。

Edit:

片段的 ViewModel 源:

viewModel = getSharedViewModel<OtpViewModel>(from = {
            Navigation.findNavController(container as View).getViewModelStoreOwner(R.id.two_step_authentication_graph)
        })

对话框片段的 ViewModel 源:

viewModel = getViewModel<OtpViewModel>()

如果我理解正确的话,几个月前,作为 Jetpack 库和 Kotlin 的新手,我遇到了类似的问题。

我认为这里的问题是您正在使用以下方法检索 ViewModelby viewModels这意味着您返回的 ViewModel 将仅适用于当前片段上下文...如果您想在应用程序的多个部分之间共享视图模型,它们必须是活动范围的。

例如:

//this will only work for the current fragment, using this declaration here and anywhere else and observing changes wont work, the observer will never fire, except if the method is called within the same fragment that this is declared
private val viewModel: AddPatientViewModel by viewModels {
    InjectorUtils.provideAddPatientViewModelFactory(requireContext())
}

//this will work for the ANY fragment in the current activies scope, using this code and observing anywhere else should work, the observer will fire, except if the method is called fro another activity
private val patientViewModel: PatientViewModel by activityViewModels {
    InjectorUtils.providePatientViewModelFactory(requireContext())
}

注意我的viewModel类型的AddPatientViewModel仅通过以下方式将范围限制为当前片段上下文viewModel: XXX by viewModels,对该特定 ViewModel 所做的任何更改等都只会在我当前的片段中传播。

然而patientViewModel类型的PatientViewModel范围仅限于活动上下文patientViewModel: XXX by activityViewModels。 这意味着只要两个片段属于同一个活动,并且您通过以下方式获取 ViewModel... by activityViewModels您应该能够在全局范围内观察对 ViewModel 所做的任何更改(全局意味着声明它的同一活动中的任何片段)。

考虑到上述所有内容,如果您的 viewModel 的范围正确地限定在您的 Activity 中,并且在两个片段中,您可以使用以下方法检索 viewModelby activityViewModels并通过更新观察到的值XXX.postValue(YYY) or XXX.value = YYY您应该能够从同一活动上下文中的任何位置观察对 ViewModel 所做的任何更改。

希望这是有道理的,现在已经很晚了,我在睡觉之前看到了这个问题!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

LiveData无法观察到变化 的相关文章

随机推荐