导航组件片段在第一次执行后卡在 运行 最后几行代码
Navigation Component Fragment stuck running last lines of code after 1-st execution
我有 1 个 activity 和 3 个片段:签名 In/Sign Up/Forgot 密码
登录,这是这 3 个中的主要片段,工作正常,但是,我在使用登录 Up/Forgot 密码片段时遇到问题。
目前我有这个导航组件:
my nav component
登录片段:
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 和 Success 消息。 (但我没有收到消息)所以 Fragment 卡住了,除非我重新启动应用程序,否则我再也无法进入内部。
我不知道这是什么原因以及如何解决这个问题,请在这里帮助我。
在导航成功或失败案例之前关闭警报对话框。
问题出在这里:
private val loginViewModel: LoginViewModel by activityViewModels()
by activityViewModels()
使状态在以这种方式引用 ViewModel 的所有片段之间持续存在。
只需将activityViewModels()
替换为viewModels()
我有 1 个 activity 和 3 个片段:签名 In/Sign Up/Forgot 密码
登录,这是这 3 个中的主要片段,工作正常,但是,我在使用登录 Up/Forgot 密码片段时遇到问题。
目前我有这个导航组件:
my nav component
登录片段:
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 和 Success 消息。 (但我没有收到消息)所以 Fragment 卡住了,除非我重新启动应用程序,否则我再也无法进入内部。
我不知道这是什么原因以及如何解决这个问题,请在这里帮助我。
在导航成功或失败案例之前关闭警报对话框。
问题出在这里:
private val loginViewModel: LoginViewModel by activityViewModels()
by activityViewModels()
使状态在以这种方式引用 ViewModel 的所有片段之间持续存在。
只需将activityViewModels()
替换为viewModels()