Android:切换片段后按钮 onClicklistener 不工作
Android: Button onClicklistener not working after switching fragment
我有一个很奇怪的问题。当我使用 btn.setOnClickListener
从 Fragment 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)
}
}
我试图删除对问题不重要的所有内容。你可以忽略BaseFragment
、EmailFragment
、CalibrateRepairMessageFragment
和CalibrateRepairUserDataFragment
的构造函数。我正在使用导航组件和匕首柄。
非常感谢您的帮助,谢谢。
P.S:我注意到在 .xml 文件中使用 button:onClick
可以解决这个问题,但在这种情况下我无法使用 xml 版本.
这个问题应该是你 btnNext
.
的延迟初始化
导航到 Fragment2
时保存 Fragment1
状态。返回时,XML 视图将被重新加载,但 btnNext
的惰性值不会改变,因为它已经初始化并指向按钮视图的旧引用。因此,您的 OnClickListener
将始终设置为旧参考。
与其懒惰地分配按钮,不如在 EmailFragment
的 onCreateView()
中分配它
PS:同样来自 btn_next_calibrate
我想您正在使用 kotlin 合成视图绑定。如果是这样,您就不必使用 class 变量。
我有一个很奇怪的问题。当我使用 btn.setOnClickListener
从 Fragment 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)
}
}
我试图删除对问题不重要的所有内容。你可以忽略BaseFragment
、EmailFragment
、CalibrateRepairMessageFragment
和CalibrateRepairUserDataFragment
的构造函数。我正在使用导航组件和匕首柄。
非常感谢您的帮助,谢谢。
P.S:我注意到在 .xml 文件中使用 button:onClick
可以解决这个问题,但在这种情况下我无法使用 xml 版本.
这个问题应该是你 btnNext
.
导航到 Fragment2
时保存 Fragment1
状态。返回时,XML 视图将被重新加载,但 btnNext
的惰性值不会改变,因为它已经初始化并指向按钮视图的旧引用。因此,您的 OnClickListener
将始终设置为旧参考。
与其懒惰地分配按钮,不如在 EmailFragment
的 onCreateView()
PS:同样来自 btn_next_calibrate
我想您正在使用 kotlin 合成视图绑定。如果是这样,您就不必使用 class 变量。