我正在从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(重新发送按钮click),并需要在片段中读取它。所以我使用了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>()
答案 0 :(得分:0)
问题在于您实际上并没有在 Fragment 和 Dialog 之间共享 ViewModel 。要共享 ViewModel 的实例,必须从相同的sorted categories: [['baby', 'boy', 'child', 'daughter', 'girl', 'king', 'man', 'queen', 'son', 'woman']]
[[0. 0. 0. 0. 0. 0. 1. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 1. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
[0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
[0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]]
中检索它们。
您用于检索 ViewModels 的语法似乎来自第三方框架。我觉得可能是Koin。
如果是这种情况,请注意,在 Koin 中,ViewModelStore
从 Fragment 自己的< em> ViewModelStore 。因此,您正在从自己的 ViewModelStore 中获取 DialogFragment 中的 ViewModel 。另一方面,在您的 Fragment 中,您正在使用getViewModel
对其进行检索,您可以在其中指定应检索 ViewModel <的 ViewModelStore 。 / em>来自。因此,您正在从两个不同的 ViewModelStores 中检索 ViewModel ,因此,将获得两个不同的 ViewModel 。与其中一个进行交互不会影响另一个,因为它们不是同一实例。
要解决此问题,您应该从同一 ViewModelStore 的 Fragment 和 DialogFragment 中都检索 ViewModel 。例如,您可以在两者中都使用getSharedViewModel
,或者在每个位置手动指定相同的 ViewModelStore ,甚至甚至不指定哪个 Koin 将默认设置为活动的一项。
您甚至还可以在 Fragment 中使用getSharedViewModel
,然后将其自己的特定 ViewModelStore 传递给 DialogFragment ,在然后您可以使用getViewModel
,指定传递的 Fragment 的 ViewModelStore 。
答案 1 :(得分:0)
几个月前,当我重新认识Jetpack库和Kotlin时,如果我对您的理解正确,就会遇到类似的问题。
我认为这里的问题是您正在使用by viewModels
来检索ViewModel,这意味着您返回的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所做的任何更改等都只会在我的当前片段中传播。 / p>
类型为patientViewModel
的{{1}}通过PatientViewModel
限定于活动上下文。
这意味着,只要两个片段都属于同一个活动,并且您通过patientViewModel: XXX by activityViewModels
获得了ViewModel,则您应该能够在全局范围内观察对ViewModel所做的任何更改(全局意味着同一活动中的任何片段在那里声明)。
请牢记以上所有内容,如果您的viewModel正确地限定在您的活动范围内,并且在两个片段中,您都使用... by activityViewModels
检索viewModel并更新了通过by activityViewModels
或{{1}观察到的值},您应该能够在同一活动上下文中的任何地方观察到对ViewModel所做的任何更改。
希望如此,这已经很晚了,我在打麻袋之前就看到了这个问题!