简体   繁体   中英

Android: Button onClicklistener not working after switching fragment

I have a very weird problem. When I navigate from Fragment 1 to Fragment 2 using a btn.setOnClickListener and then navigating back from Fragment 2 to Fragment 1 using the back button, the btn.setOnClickListener in Fragment 1 does not work anymore and therefore is not able to navigate to Fragment 2 again.

Here is my code:

Button XML

<?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>

Main XML

<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 for databinding

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 for initializing the button

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")
        }
    }
}

Fragment 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
    }
}

Fragment 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)
    }
}

I tried to delete everything that is not important for the question. You can ignore the constructor of BaseFragment , EmailFragment , CalibrateRepairMessageFragment and CalibrateRepairUserDataFragment . I am using navigation component and dagger-hilt.

I appreciate every help, thank you.

PS: I've noticed that using button:onClick in the .xml file solves this problem but in this situation I can't use the xml version.

The problem with this should be your lazy initialization of btnNext .

The Fragment1 state is saved when navigating to Fragment2 . When navigating back the XML View will be reloaded but the lazy value of btnNext won't change as it is already initialized and is pointing to the old reference of the button view. Thus your OnClickListener will always be set to the old reference.

Instead of assigning your button lazily you should assign it in EmailFragment 's onCreateView()

PS: Also from btn_next_calibrate I suppose you are using kotlin synthetic view binding. If so you would not have to use a class variable.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM