android - 导航组件片段在第一次执行后卡在运行最后一行代码
问题描述
我有 1 个包含 3 个片段的活动:登录/注册/忘记密码
登录,这是这三个工作中的主要片段,但是,我在使用注册/忘记密码片段时遇到了问题。
目前我有这个导航组件:
登录片段:
class SignInFragment : Fragment() {
private val loginViewModel: LoginViewModel by activityViewModels()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_sign_in, container, false)
// see "ProgressButton" in dependencies of build.gradle file.
// used for more interactive button.
bindProgressButton(view.button_sign_in_submit)
loginViewModel.signInState.observe(viewLifecycleOwner, { state ->
when (state) {
is UserSignInState.Loading -> view.button_sign_in_submit.showProgress {
buttonTextRes = R.string.status_loading
progressColor = Color.WHITE
}
is UserSignInState.SignedIn -> {
view.button_sign_in_submit.hideProgress(R.string.button_sign_in_submit)
startActivity(Intent(this.activity, ProfileActivity::class.java))
this.activity?.finish()
}
is UserSignInState.Failure -> {
view.button_sign_in_submit.hideProgress(R.string.button_sign_in_submit)
view.sign_in_input_password_layout.error = state.exception.localizedMessage
}
}
})
// Used for resetting focus and hiding keyboard for better user experience.
view.sign_in_input_email.setOnFocusChangeListener { _, hasFocus -> if (!hasFocus) hideKeyboard() }
view.sign_in_input_password.setOnFocusChangeListener { _, hasFocus -> if (!hasFocus) hideKeyboard() }
view.button_go_to_sign_up.setOnClickListener {
hideKeyboard()
this.findNavController().navigate(R.id.action_signInFragment_to_signUpFragment)
}
view.button_go_to_forgot_password.setOnClickListener {
hideKeyboard()
this.findNavController().navigate(R.id.action_signInFragment_to_forgotPasswordFragment)
}
view.button_sign_in_submit.setOnClickListener {
hideKeyboard()
view.sign_in_input_email_layout.error = null
view.sign_in_input_password_layout.error = null
val email = view.sign_in_input_email.text.toString()
val password = view.sign_in_input_password.text.toString()
if (email.isNotEmpty() && password.isNotEmpty()) {
if (isEmailValid(email)) {
loginViewModel.userSignIn(email, password)
} else {
view.sign_in_input_email_layout.error = getString(R.string.error_message_email_is_not_valid)
}
} else {
view.sign_in_input_password_layout.error = getString(R.string.error_message_please_fill_in_all_fields)
}
}
return view
}
}
忘记密码片段:
class ForgotPasswordFragment : Fragment() {
private val loginViewModel: LoginViewModel by activityViewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_forgot_password, container, false)
// see "ProgressButton" in dependencies of build.gradle file.
// used for more interactive button.
bindProgressButton(view.button_forgot_password_submit)
this.retainInstance = false
loginViewModel.passwordResetState.observe(viewLifecycleOwner, { state ->
when (state) {
is UserPasswordResetState.Loading -> view.button_forgot_password_submit.showProgress {
buttonTextRes = R.string.status_loading
progressColor = Color.WHITE
}
is UserPasswordResetState.Sent -> {
view.button_forgot_password_submit.hideProgress(R.string.button_forgot_password_submit)
alertDialogSuccess(requireContext(), getString(R.string.alert_dialog_message_forgot_password_email_has_been_sent))
activity?.findNavController(R.id.nav_host_fragment_login)?.navigate(R.id.action_forgotPasswordFragment_to_signInFragment)
}
is UserPasswordResetState.Error -> {
view.button_forgot_password_submit.hideProgress(R.string.button_forgot_password_submit)
// Could be possibly replaced with implementation of "alertDialogFailure"
view.forgot_password_input_field_email_layout.error = state.exception.localizedMessage
}
}
})
view.forgot_password_input_field_email.setOnFocusChangeListener { _, hasFocus -> if (!hasFocus) hideKeyboard() }
view.button_forgot_password_submit.setOnClickListener {
hideKeyboard()
if (isEmailValid(view.forgot_password_input_field_email.text.toString())) {
loginViewModel.userResetPassword(view.forgot_password_input_field_email.text.toString())
} else if (!isEmailValid(view.forgot_password_input_field_email.text.toString())) {
view.forgot_password_input_field_email_layout.error = getString(R.string.error_message_email_is_not_valid)
} else {
view.forgot_password_input_field_email_layout.error = getString(R.string.error_message_unknown)
}
}
return view
}
}
导航 XML
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_graph_login"
app:startDestination="@id/signInFragment">
<fragment
android:id="@+id/signInFragment"
android:name="my.test.movieexpert._login.view.fragments.SignInFragment"
android:label="fragment_sign_in"
tools:layout="@layout/fragment_sign_in">
<action
android:id="@+id/action_signInFragment_to_signUpFragment"
app:destination="@id/signUpFragment"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim" />
<action
android:id="@+id/action_signInFragment_to_forgotPasswordFragment"
app:destination="@id/forgotPasswordFragment"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim" />
</fragment>
<fragment
android:id="@+id/signUpFragment"
android:name="my.test.movieexpert._login.view.fragments.SignUpFragment"
android:label="fragment_sign_up"
tools:layout="@layout/fragment_sign_up">
<action
android:id="@+id/action_signUpFragment_to_signInFragment"
app:destination="@id/signInFragment"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popUpTo="@+id/signInFragment"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="@+id/forgotPasswordFragment"
android:name="my.test.movieexpert._login.view.fragments.ForgotPasswordFragment"
android:label="fragment_forgot_password"
tools:layout="@layout/fragment_forgot_password">
<action
android:id="@+id/action_forgotPasswordFragment_to_signInFragment"
app:destination="@id/signInFragment"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popUpTo="@+id/signInFragment"
app:popUpToInclusive="true" />
</fragment>
</navigation>
会发生什么:我启动我的应用程序。按 - 忘记密码,然后输入我的电子邮件,我收到电子邮件已发送的警报对话框,然后我被转移到我的登录屏幕。但是,当我下次按“忘记密码”时,我看不到相同的字段等,而我得到的只是上次运行的带有成功消息的 AlertDialog。(但我没有收到消息)所以 Fragment 卡住了,除非我重新启动应用程序,否则我永远无法再次进入。
我不知道这是什么原因以及如何解决这个问题,请在这里帮助我。
解决方案
问题出在这里:
private val loginViewModel: LoginViewModel by activityViewModels()
by activityViewModels()
使状态在以这种方式引用 ViewModel 的所有 Fragment 之间保持不变。
只需替换activityViewModels()
为viewModels()
推荐阅读
- reactjs - 构造函数和渲染方法在反应组件中运行两次
- swift - Firestore 订单和限制数据返回缺失的结果
- database - 使用 Symfony 查询 On The Fly 解密数据
- php - 通过 Web 界面在 Linux 上挂载 CIFS 共享
- bash - 如何在linux中计算大于8000的选项卡之间的字符
- sqlite - SQLite 查询两个表。(Union All,Order By,Limit)
- javascript - 本地存储中每个项目的按钮
- c# - 有没有办法删除整个图片框数组列表(也删除图像)并创建一个新的?
- rust - 如何在 Rocket 响应器中转发来自 reqwest::blocking::Body 的数据?
- apache - HTTPD 不会在 Centos OS 7/Apache 2.4.6 上启动