Android: На BackPressed Fragment не загружается контент - PullRequest
0 голосов
/ 21 февраля 2020

Итак, я использую 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()
    }

1 Ответ

1 голос
/ 21 февраля 2020

Вы получаете контекст только после того, как фрагмент прикреплен к действию.
Когда вызывается onCreateView, у вас еще нет контекста, и он возвращает:

override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val binding = FragmentRegisterBinding.inflate(inflater, container, false)
        context ?: return binding.root
        // ...
}

Вы должны переместиться ваши настройки логи c до onViewCreated:

lateinit var binding: FragmentRegisterBinding 

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    binding = FragmentRegisterBinding.inflate(inflater, container, false)
    return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    injectFeature()

    setToolbar(binding)

    subscribeUi(binding)
}
...