Tengo un problema muy extraño. Cuando navego de Fragment 1 a Fragment 2 usando un btn.setOnClickListener y luego regreso de Fragment 2 a Fragment 1 usando el botón de retroceso, el btn.setOnClickListener en Fragment 1 ya no funciona y, por lo tanto, no puede navegar a Fragment 2 nuevamente.

Aquí está mi código:

XML de botón

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">

// Custom Background for the button
<com.google.android.material.appbar.MaterialToolbar
        android:clickable="false"
        android:id="@+id/materialToolbar"
        android:layout_width="match_parent"
        android:layout_height="90dp"
        android:background="@color/btnColorGray"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent">
    </com.google.android.material.appbar.MaterialToolbar>

    <com.google.android.material.button.MaterialButton
        android:clickable="true"
        android:focusable="true" />

</androidx.constraintlayout.widget.ConstraintLayout>

XML principal

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.view.fragments.home.calibrateAndRepair.CalibrateRepairMessageFragment">

    ... some other stuff

    <!-- Included the button -->
    <include
        android:id="@+id/calibrate_repair_btn"
        layout="@layout/calibrate_repair_btn"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

BaseFragment para el enlace de datos

abstract class BaseFragment<out T: ViewDataBinding>(val layout: Int) : Fragment() {
    abstract val viewModel: ViewModel
    private val _navController by lazy { findNavController() }
    val navController: NavController
        get() = _navController

    
    fun navigateTo(fragment: Int, bundle: Bundle? = null) {
        _navController.navigate(fragment, bundle)
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return DataBindingUtil.inflate<T>(inflater, layout, container, false).apply {
            lifecycleOwner = viewLifecycleOwner
            setVariable(BR.viewModel, viewModel)
            Timber.d("Created BaseFragment and binded View")
        }.root
    }
}

EmailFragment para inicializar el botón

abstract class EmailFragment<out T: ViewDataBinding>(
    layout: Int,
    private val progressBarDescription: ArrayList<String>,
    private val stateNumber: StateProgressBar.StateNumber
) : BaseFragment<T>(layout) {

    abstract val next: Int
    abstract val bundleNext: Bundle?
    // getting the button from the button.xml
    private val btnNext: MaterialButton by lazy { btn_next_calibrate }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // ... some other initializing which constantly work!
        initButton()
    }

    // Initializing the button
    private fun initButton() {
        btnNext.setOnClickListener {
            navigateTo(next, bundleNext)
            Timber.d("Button clicked")
        }
    }
}

Fragmento 1

@AndroidEntryPoint
class CalibrateRepairMessageFragment(
    private val progressBarDescription: ArrayList<String>,
    @StateNumberOne private val stateNumber: StateProgressBar.StateNumber,
) : EmailFragment<FragmentCalibrateRepairMessageBinding>(
    R.layout.fragment_calibrate_repair_message,
    progressBarDescription,
    stateNumber
) {
    // Overriding the values from EmailFragment
    override val next: Int by lazy { R.id.action_calibrateRepairMessageFragment_to_calibrateRepairUserDataFragment }
    override val bundleNext: Bundle by lazy { bundleOf("calibrate_repair_toolbar_text" to toolbarText) }

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

        // ... some other stuff
    }
}

Fragmento 2

@AndroidEntryPoint
class CalibrateRepairUserDataFragment(
    private val progressBarDescription: ArrayList<String>,
    @StateNumberTwo private val stateNumber: StateProgressBar.StateNumber,
) : EmailFragment<FragmentCalibrateRepairUserDataBinding>(
    R.layout.fragment_calibrate_repair_user_data,
    progressBarDescription,
    stateNumber
) {
    override val next: Int by lazy { R.id.action_calibrateRepairUserDataFragment_to_calibrateRepairDataOverviewFragment }
    override val bundleNext: Bundle by lazy { bundleOf("calibrate_repair_toolbar_text" to toolbarText) }


    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
    }
}

Intenté eliminar todo lo que no es importante para la pregunta. Puede ignorar el constructor de BaseFragment, EmailFragment, CalibrateRepairMessageFragment y CalibrateRepairUserDataFragment. Estoy usando el componente de navegación y la empuñadura de la daga.

Agradezco cada ayuda, gracias.

PD: He notado que usar button:onClick en el archivo .xml resuelve este problema, pero en esta situación no puedo usar la versión xml.

0
Andrew 28 ago. 2020 a las 14:37

1 respuesta

La mejor respuesta

El problema con esto debería ser su inicialización perezosa de btnNext.

El estado Fragment1 se guarda cuando se navega a Fragment2. Al navegar hacia atrás, la Vista XML se recargará, pero el valor diferido de btnNext no cambiará, ya que ya está inicializado y apunta a la referencia anterior de la vista de botones. Por lo tanto, su OnClickListener siempre se establecerá en la referencia anterior.

En lugar de asignar su botón de forma perezosa, debe asignarlo en EmailFragment onCreateView()

PD: También de btn_next_calibrate supongo que estás usando el enlace de vista sintético de kotlin. Si es así, no tendría que utilizar una variable de clase.

1
CodeRed 28 ago. 2020 a las 12:06