Итак, я использую Android Navigation component
внутри своего проекта. У меня есть активность с 3 фрагментами, которые отлично загружаются и делают все, что им нужно сделать. Проблема в том, что один фрагмент не загружает свое содержимое при возврате из backstack.
Я использую navigate
функции, объявленные в ViewModel
с Directions
из Navigation
. пример (vm.navigate(SomeFragmentDirections.actionSomeFragmentToOtherFragment
)
A (активность) -> B (фрагмент) -> C (фрагмент) -> D (фрагмент)
, когда я нажимаю на D * От 1013 * до go обратно к C fragment
он показывает верхний navbar
, но не загружает его содержимое. Я использую те же принципы для всех других видов деятельности / фрагментов в других моих проектах (даже в этом), и у меня нет этой проблемы. Все lifecycle
функции вызываются и все должно работать нормально. LogCat не показывает никаких ошибок вообще. Если кто-нибудь что-нибудь знает об этом, я был бы признателен.
РЕДАКТИРОВАТЬ:
Это фрагмент, который не загружается (Фрагмент C) Фрагмент D является фрагментом webView, фрагмент C переходит к нему в функции vm.navigate(RegisterFragmentDirections.actionRegisterFragmentToWebViewFragment(webURL))
class RegisterFragment : BaseFragment() {
private val vm: RegisterViewModel by viewModel()
override fun getViewModel(): BaseViewModel = vm
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = FragmentRegisterBinding.inflate(inflater, container, false)
context ?: return binding.root
injectFeature()
setToolbar(binding)
subscribeUi(binding)
return binding.root
}
/**
* set toolbar
* **/
private fun setToolbar(binding: FragmentRegisterBinding) {
if (activity is WelcomeActivity) {
binding.appBarLayout.backClickListener = (activity as WelcomeActivity).createOnBackClickListener()
} else if(activity is LoginActivity) {
binding.appBarLayout.backClickListener = (activity as LoginActivity).createOnBackClickListener()
}
}
/**
* set ui
* **/
private fun subscribeUi(binding: FragmentRegisterBinding) {
// set bindings
binding.contentRegister.viewModel = vm
binding.contentSuccess.viewOwner = this
// set true full screen
(activity as LoginActivity).setFullScreen(false)
// set dark status bar icons
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
activity!!.window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
}
// set initial margin top
ViewCompat.setOnApplyWindowInsetsListener(binding.rootLayout) { _, insets ->
binding.appBarLayout.toolbar.setMarginTop(insets.systemWindowInsetTop)
insets
}
// set phone number mask listener
binding.contentRegister.etPhoneNumber.addTextChangedListener(PhoneNumberFormattingTextWatcher())
// set licence agreement formatted text with hyperlinks
setTextViewHTML(binding.contentRegister.licenceAgreement, getString(R.string.description_privacy_with_link))
// set listener on form elements, error handling
binding.contentRegister.etName.onFocusChangeListener = emptyInputValidationListener(
binding.contentRegister.etName,
binding.contentRegister.tilName,
getString(R.string.error_empty_name)
)
binding.contentRegister.etLastName.onFocusChangeListener = emptyInputValidationListener(
binding.contentRegister.etLastName,
binding.contentRegister.tilLastName,
getString(R.string.error_empty_last_name)
)
binding.contentRegister.etBirthDate.setOnFocusChangeListener { _, hasFocus ->
if (!hasFocus) {
when(isBirthDateValid(binding.contentRegister.etBirthDate.text.toString())) {
false -> binding.contentRegister.tilBirthDate.error = getString(R.string.error_date_not_valid)
true -> binding.contentRegister.tilBirthDate.isErrorEnabled = false
}
}
}
binding.contentRegister.etEmail.setOnFocusChangeListener { _, hasFocus ->
if (!hasFocus) {
when(!android.util.Patterns.EMAIL_ADDRESS.matcher(binding.contentRegister.etEmail.text!!.trim()).matches()) {
true -> binding.contentRegister.tilEmail.error = getString(R.string.error_email_not_valid)
false -> binding.contentRegister.tilEmail.isErrorEnabled = false
}
}
}
binding.contentRegister.etPassword.setOnFocusChangeListener { _, hasFocus ->
if (!hasFocus) {
when (binding.contentRegister.etPassword.text!!.trim().length < 6) {
true -> binding.contentRegister.tilPassword.error =
getString(R.string.error_password_not_valid)
false -> binding.contentRegister.tilPassword.isErrorEnabled = false
}
}
}
binding.contentRegister.registerButton.setOnClickListener{
validateInputs(binding)
}
// set observables
vm.userResponse.observe(viewLifecycleOwner, Observer { updateRegisterSuccess(binding, it) })
}
/**
* update on success / failure
* **/
private fun updateRegisterSuccess(
binding: FragmentRegisterBinding,
resource: Resource<BaseResponseEntity>?
) {
resource?.let {
when (it.state) {
ResourceState.LOADING -> {
binding.contentProgress.isLoading = true
setViewAndChildrenEnabled(binding.rootLayout, false)
}
ResourceState.SUCCESS -> {
binding.contentProgress.isLoading = false
setViewAndChildrenEnabled(binding.rootLayout, true)
}
ResourceState.ERROR -> {
binding.contentProgress.isLoading = false
setViewAndChildrenEnabled(binding.rootLayout, true)
}
}
it.data?.let {
when(it.responseCode) {
RESPONSE_CODE_SUCCESS -> {
binding.contentSuccess.isSucceeded = true
setViewAndChildrenEnabled(binding.rootLayout, true)
}
RESPONSE_CODE_ERROR -> {
if (it.message.isNotEmpty()) {
showSnackbar(it.message, Snackbar.LENGTH_SHORT)
} else {
showSnackbar(getString(R.string.error_unknown), Snackbar.LENGTH_SHORT)
}
}
}
}
it.message?.let {
showSnackbar(getString(R.string.error_unknown), Snackbar.LENGTH_SHORT)
}
}
}
/**
* disable ui elements while loading
* **/
private fun setViewAndChildrenEnabled(view: View, enabled: Boolean) {
view.isEnabled = enabled
if (view is ViewGroup) {
for (i in 0 until view.childCount) {
val child = view.getChildAt(i)
setViewAndChildrenEnabled(child, enabled)
}
}
}
/**
* validate all inputs
* **/
private fun validateInputs(binding: FragmentRegisterBinding) {
// check if all inputs are valid
if(binding.contentRegister.etName.text!!.trim().isEmpty()) {
binding.contentRegister.etName.requestFocus()
binding.contentRegister.tilName.error = getString(R.string.error_empty_name)
return
}
if(binding.contentRegister.etLastName.text!!.trim().isEmpty()) {
binding.contentRegister.etLastName.requestFocus()
binding.contentRegister.tilLastName.error = getString(R.string.error_empty_last_name)
return
}
if (binding.contentRegister.etBirthDate.rawText.isNotEmpty()) {
if (!isBirthDateValid(binding.contentRegister.etBirthDate.text.toString())) {
binding.contentRegister.etBirthDate.requestFocus()
binding.contentRegister.tilBirthDate.error =
getString(R.string.error_date_not_valid)
return
}
}
if(!android.util.Patterns.EMAIL_ADDRESS.matcher(binding.contentRegister.etEmail.text!!.trim()).matches()) {
binding.contentRegister.etEmail.requestFocus()
binding.contentRegister.tilEmail.error = getString(R.string.error_date_not_valid)
return
}
if(binding.contentRegister.etPassword.text!!.trim().length < PASSWORD_MINIMUM_LENGHT) {
binding.contentRegister.etPassword.requestFocus()
binding.contentRegister.tilPassword.error = getString(R.string.error_password_not_valid)
return
}
if(!binding.contentRegister.checkBox.isChecked) {
showSnackbar(getString(R.string.error_terms_and_conditions), Snackbar.LENGTH_SHORT)
return
}
// handle date of birth
val dateOfBirth = if (binding.contentRegister.etBirthDate.rawText.trim().isNotEmpty()
&& isBirthDateValid(binding.contentRegister.etBirthDate.rawText)) {
binding.contentRegister.etBirthDate.text.toString().replace("/", "-")
} else {
""
}
binding.rootLayout.hideKeyboard()
vm.register(
username = binding.contentRegister.etEmail.text.toString(),
password = binding.contentRegister.etPassword.text.toString(),
name = binding.contentRegister.etName.text.toString(),
lastName = binding.contentRegister.etLastName.text.toString(),
phoneNumber = binding.contentRegister.etPhoneNumber.text.toString(),
dateOfBirth = dateOfBirth)
Timber.d(dateOfBirth)
}
//todo handle this and move to util class
@Suppress("DEPRECATION")
private fun setTextViewHTML(text: TextView, html: String) {
// replace \n new line so android can show new line for text which we previously fetchCompanies from server
val hmtlFormatted = html.replace("\n", "<br>")
val sequence = Html.fromHtml(hmtlFormatted)
val strBuilder = SpannableStringBuilder(sequence)
val urls = strBuilder.getSpans(0, sequence.length, URLSpan::class.java)
for (span in urls) {
makeLinkClickable(strBuilder, span)
}
text.text = strBuilder
text.movementMethod = LinkMovementMethod.getInstance()
}
private fun makeLinkClickable(strBuilder: SpannableStringBuilder, span: URLSpan) {
val start = strBuilder.getSpanStart(span)
val end = strBuilder.getSpanEnd(span)
val flags = strBuilder.getSpanFlags(span)
val clickable = object : ClickableSpan() {
override fun onClick(view: View) {
// Do something with span.getURL() to handle the link click...
val webURL = span.url
vm.navigate(RegisterFragmentDirections.actionRegisterFragmentToWebViewFragment(webURL))
}
}
strBuilder.setSpan(clickable, start, end, flags)
strBuilder.removeSpan(span)
}
// PUBLIC ACTIONS ---
fun onRegisterDoneClick() {
// navigate to welcome activity and finish it
onRegisterSuccess()
}
/**
* on register success
* **/
private fun onRegisterSuccess() {
// navigate to welcome activity and finish it
val returnIntent = Intent()
(activity as LoginActivity).setResult(Activity.RESULT_OK, returnIntent)
(activity as LoginActivity).finish()
}