Android:切换片段后按钮 onClicklistener 不工作

Android: Button onClicklistener not working after switching fragment

我有一个很奇怪的问题。当我使用 btn.setOnClickListenerFragment 1 导航到 Fragment 2,然后使用后退按钮从 Fragment 2 导航回 Fragment 1 时,btn.setOnClickListener Fragment 1 不再工作,因此无法再次导航到 Fragment 2

这是我的代码:

按钮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>

主要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

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

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

片段 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
    }
}

片段 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)
    }
}

我试图删除对问题不重要的所有内容。你可以忽略BaseFragmentEmailFragmentCalibrateRepairMessageFragmentCalibrateRepairUserDataFragment的构造函数。我正在使用导航组件和匕首柄。

非常感谢您的帮助,谢谢。

P.S:我注意到在 .xml 文件中使用 button:onClick 可以解决这个问题,但在这种情况下我无法使用 xml 版本.

这个问题应该是你 btnNext.

的延迟初始化

导航到 Fragment2 时保存 Fragment1 状态。返回时,XML 视图将被重新加载,但 btnNext 的惰性值不会改变,因为它已经初始化并指向按钮视图的旧引用。因此,您的 OnClickListener 将始终设置为旧参考。

与其懒惰地分配按钮,不如在 EmailFragmentonCreateView()

中分配它

PS:同样来自 btn_next_calibrate 我想您正在使用 kotlin 合成视图绑定。如果是这样,您就不必使用 class 变量。