从视图模型中移除当前片段并启动另一个片段



下面是我的ViewModel类,它接受application: application作为参数。我想从这个类中启动另一个片段。但是在remove()方法中,我如何传递fragment.

class EmailConfirmationFragmentViewModel(application: Application) : AndroidViewModel(application) {
private lateinit var viewModelApplication: Application
init {
this.viewModelApplication = application
}
var email = MutableLiveData<String>()
private var emailMutableLiveData: MutableLiveData<UserEmail>? = null
val userEmail: MutableLiveData<UserEmail>
get() {
if (emailMutableLiveData == null) {
emailMutableLiveData = MutableLiveData<UserEmail>()
}
return emailMutableLiveData!!
}
fun onEmailChanged(s: CharSequence, start: Int, before: Int, count: Int) {
if (s.toString() != null && !s.toString().equals(""))
email.value = s.toString()
}
fun onConfirmClicked(view: View) {
userEmail.value = UserEmail(email.value.toString())
launchResetPasswordFragment()
}
private fun launchResetPasswordFragment() {
try {
(viewModelApplication as FragmentActivity).supportFragmentManager.beginTransaction()
.replace(R.id.fl_Wrapper, OtpVerificationFragement()).remove(viewModelApplication.applicationContext).commit()
}
catch(e:Exception)
{
Log.e("Error","$e")
}
}

}

生命周期事件和片段事务不应该发生在视图模型内部。正如在ViewModel概述中所讨论的,ViewModel绝对不能引用视图、生命周期或任何可能包含对活动上下文引用的类。虽然AndroidViewModel确实通过向应用程序公开引用引入了反模式,但这个特定的用例并不合适。在视图模型应该调用片段事务的情况下,它通常由从视图模型分派给Lifecycle Owner的事件的一般概念来处理。我相信采用这种模式可以解决您的问题。虽然我不知道你的碎片的状态,但我已经想出了一个可能的解决方案。

class EmailConfirmationViewModel() : ViewModel() {
val email: MutableLiveData<String> = MutableLiveData()
private val _resetFragment: MutableLiveData<Event> = MutableLiveData()
val resetFragment: LiveData<Event> = _resetFragment
val userEmail: UserEmail?
get() = email.value?.let { UserEmail(it) }
fun onEmailChanged(s: CharSequence) {
email.value = s.toString()
}
fun onConfirmClicked() {
resetFragment()
}
private fun resetFragment() {
_resetFragment.value = Event()
}
}

支持的事件类可以这样出现:

class Event : EventWithValue<Unit>(Unit)
open class EventWithValue<T>(
private val value: T,
) {
private var isHandled = false
fun getValueIfUnhandled(): T? = if (isHandled) {
null
} else {
handleValue()
}
private fun handleValue(): T {
isHandled = true
return value
}
}
class EventObserver<T>(
private val eventIfUnhandled: (value: T) -> Unit,
) : Observer<EventWithValue<T>?> {
override fun onChanged(event: EventWithValue<T>?) {
event?.getValueIfUnhandled()?.let { eventIfUnhandled(it) }
}
}

通过观察Fragment本身中的事件,您消除了在视图模型中引用任何类型视图的需要,同时保持视图模型作为调度程序的角色。这里有一个简短的描述,你将如何从你的生命周期所有者那里监听事件,在这个例子中,是一个片段。

class EmailConfirmationFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view: View? = super.onCreateView(inflater, container, savedInstanceState)
val viewModel: EmailConfirmationViewModel by viewModels()
viewModel.resetFragment.observe(viewLifecycleOwner, EventObsever {
// Call a function of the activity's viewModel (ideal), or complete the transaction here through referencing the activity directly (ill-advised)
})
return view
}
}

我认为暴露userEmail本身就是一种代码气味。或者,您可以将resetFragment事件定义为

private val _resetFragment: MutableLiveData<EventWithValue<UserEmail>> = MutableLiveData()
val resetFragment: LiveData<EventWithValue<UserEmail>> = _resetFragment

并直接在上面的事件侦听器中接收userEmail的值。这样就不需要公开视图模型的userEmail了。

最新更新