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

ViewModel:

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)

    }
}

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

        })
    }
}

我从 DialogFragment 的xml文件中调用viewmodel的 resendOTP(contactId:String)方法:
 <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" />

现在,每当我尝试从Fragment调用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) }
            }
        })

    }

所以我在这里做什么错。

编辑

基本上,我需要在dialogfragment中单击clicklistener(重新发送按钮单击),并需要在片段中读取它。因此,我使用了SharedViewModel的概念。

因此,我在ViewModel中进行了必要的更改:
private val selected = MutableLiveData<LogonModel>()

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

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

在DialogFragment中:
 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) }
            }
        })

但是它仍然无法正常工作。

编辑:

片段的ViewModel源:
viewModel = getSharedViewModel<OtpViewModel>(from = {
            Navigation.findNavController(container as View).getViewModelStoreOwner(R.id.two_step_authentication_graph)
        })

对话框片段的ViewModel源:
viewModel = getViewModel<OtpViewModel>()

最佳答案

问题是您实际上没有在Fragment和Dialog之间共享ViewModel。要共享ViewModel的实例,必须从相同的ViewModelStore中检索它们。

您用于检索ViewModels的语法似乎来自第三方框架。我觉得大概Koin

如果是这种情况,请注意,在Koin中,getViewModel从Fragment自己的ViewModelStore中检索ViewModel。因此,您正在从DialogFragment中的ViewModelStore中检索ViewModel。另一方面,在Fragment中,您使用getSharedViewModel对其进行检索,您可以在其中指定应从哪个ViewModelStore检索ViewModel。因此,您正在从两个不同的ViewModelStores中检索ViewModel,因此,得到了两个不同的ViewModel。与其中一个进行交互不会影响另一个,因为它们不是同一实例。

要解决此问题,您应该从同一个ViewModelStore中检索Fragment和DialogFragment中的ViewModel。例如,您可以在两者中都使用getSharedViewModel,可以在每个位置手动指定相同的ViewModelStore,或者甚至不指定哪个Koin将默认为其Activity的ViewModelStore。

您甚至还可以在Fragment中使用getViewModel,然后将其自己的特定ViewModelStore传递给DialogFragment,然后在其中使用getSharedViewModel,指定传递的Fragment的ViewModelStore。

08-04 10:06