问题描述
我正在从ViewModel的DialogFragment中更新 LiveData值,但无法在Fragment中获取该值.
I am updating a LiveData value from a DialogFragment in the ViewModel, but not able to get the value in 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)方法:
I am calling the resendOTP(contactId : String) method of the viewmodel from the xml file of the DialogFragment:
<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(重新发送按钮click),并需要在片段中读取它.因此,我使用了SharedViewModel的概念.
Basically I need clicklistener(resend button click) in dialogfragment, and need to read it in the fragment. So I used the concept of SharedViewModel.
因此,我在ViewModel中进行了必要的更改:
So I make necessary changes in the 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);
})
在我要读取值的片段中:
And in the fragment where I want to read the value:
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) }
}
})
但是它仍然无法正常工作.
But it is still not working.
片段的ViewModel源:
ViewModel Source for fragment:
viewModel = getSharedViewModel<OtpViewModel>(from = {
Navigation.findNavController(container as View).getViewModelStoreOwner(R.id.two_step_authentication_graph)
})
对话框片段的ViewModel源:
ViewModel Source for dialogfragment:
viewModel = getViewModel<OtpViewModel>()
推荐答案
几个月前,我刚接触Jetpack库和Kotlin时,如果我对您的理解正确,就会遇到类似的问题.
Being new-ish to the Jetpack library and Kotlin a few months back I ran into a similar issue, if I understand you correctly.
我认为这里的问题是您正在使用by viewModels
检索ViewModel,这意味着您返回的ViewModel仅限于当前的片段上下文...如果您想在多个视图之间共享视图模型您的应用程序的某些部分必须在活动范围内进行.
I think the issue here is that you are retrieving you ViewModel using the by viewModels
which means the ViewModel you get back will only be scoped to the current fragments context... If you would like to share a view model across multiple parts of your application they have to be activity scoped.
例如:
//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())
}
请注意,我的AddPatientViewModel
类型的viewModel
仅通过viewModel: XXX by viewModels
限制在当前片段上下文中,对该特定ViewModel所做的任何更改都只会在我的当前片段中传播.
Notice my viewModel
of type AddPatientViewModel
is scoped to the current fragments context only via viewModel: XXX by viewModels
, any changes etc made to that particular ViewModel will only be propagated in my current fragment.
类型为PatientViewModel
的patientViewModel
通过patientViewModel: XXX by activityViewModels
限定于活动上下文.这意味着,只要两个片段都属于同一个活动,并且您通过... by activityViewModels
获取了ViewModel,则您应该能够在全局范围内观察对ViewModel所做的任何更改(全局表示同一活动中的任何片段被宣布).
Where as patientViewModel
of type PatientViewModel
is scoped to the activities context via patientViewModel: XXX by activityViewModels
.This means that as long as both fragments belong to the same activity, and you get the ViewModel via ... by activityViewModels
you should be able to observe any changes made to the ViewModel on a global scope (global meaning any fragment within the same activity where it was declared).
请牢记以上所有内容,如果您的viewModel正确地限定在您的活动范围内,并且在两个片段中都使用by activityViewModels
检索了viewModel并更新了通过XXX.postValue(YYY)
或XXX.value = YYY
观察到的值,那么您应该能够在同一活动上下文中的任何位置观察ViewModel所做的任何更改.
With all the above in mind if your viewModel is correctly scoped to your activity and in both fragments you retrieve the viewModel using the by activityViewModels
and updating the value being observed via XXX.postValue(YYY)
or XXX.value = YYY
you should be able to observe any changes made to the ViewModel from anywhere within the same activity context.
希望是合理的,已经到了很晚,在我被解雇之前我就看到了这个问题!
Hope that makes sense, it's late here, and I saw this question just before I hit the sack!
这篇关于LiveData无法观察到更改的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!