我正在尝试在我的项目中实现导航组件,我遇到了这个问题,我有片段 A 和 B。我成功地从 A 转到 B,但是当我在 B 上时,我无法返回到就像我通常做的那样,就像 NavController 停止工作一样。
此外,值得一提的是,我用自己的片段 T 替换了工具栏,以便将来定义自定义行为,并且还可以动态替换。
主要活动:
@ExperimentalCoroutinesApi
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val binding: ActivityMainBinding by viewBinding(ActivityMainBinding::inflate)
private val viewmodel: MainViewmodel by viewModels()
private val toolbarFragment: BaseToolbarFragment by lazy {
BaseToolbarFragment()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
initializeToolbar()
}
/**
* method called from Fragment A
*/
fun configureToolbarForFragmentA() {
viewmodel.setBaseToolbar(resources.getString(R.string.toolbar_fragment_a_title))
}
/**
* method called from Fragment B
*/
fun configureToolbarForFragmentB() {
viewmodel.setBaseToolbar(resources.getString(R.string.toolbar_fragment_b_title), true)
}
/**
* initialize toolbar container
*/
private fun initializeToolbar() {
supportFragmentManager.beginTransaction().apply {
add(binding.toolbarContainer.id, toolbarFragment)
commit()
}
}
}
片段A:
@ExperimentalCoroutinesApi
class FragmentA :
Fragment(R.layout.fragment_a) {
private val viewModel: ViewmodelA by activityViewModels()
private val binding: FragmentABinding
by viewBinding(FragmentABinding::bind)
private lateinit var navController: NavController
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
navController = Navigation.findNavController(view)
binding.viewmodel = viewModel
setUpViews()
setUpToolbar()
observeData()
}
private fun setUpViews() {
setUpForwardButton()
setUpRadioButtons()
setUpTextInputs()
}
private fun setUpForwardButton() {
binding.forwardButton.setOnClickListener {
...
// Formulary validations
...
if (isValidated) {
viewModel.fragmentACompleted()
}
}
...
// Another Views Logic Set up
...
// Call the MainActivity method to configure toolbar for this fragment
private fun setUpToolbar() {
if (activity is MainActivity) {
(activity as MainActivity).configureToolbarForFragmentA()
}
}
/**
* [ViewmodelA.problem] LiveData is updated and the value is instance of [FormAModel]
* then go to [FragmentB]
*/
private fun observeData() {
viewModel.problem.observe(viewLifecycleOwner) {
if (it is FormAModel) {
navController.navigate(R.id.action_from_fragment_a_to_fragment_b)
}
}
}
片段B:
@ExperimentalCoroutinesApi
class FragmentB : Fragment(R.layout.fragment_b) {
private val binding : FragmentBBinding
by viewBinding(FragmentBBinding::bind)
// FragmentB and FragmentA shares the same viewmodel
private val viewmodel : ViewmodelA by activityViewModels()
private lateinit var navController: NavController
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
navController = Navigation.findNavController(view)
setUpToolbar()
requireActivity().onBackPressedDispatcher.addCallback {
// The following Commented code is what i tried (separatedly) and do nothing
//navController.popBackStack()
//navController.navigateUp()
//navController.navigate(R.id.go_back_to_createLinearProgramProblemFragment)
//navController.popBackStack(R.id.createLinearProgramProblemFragment, false)
}
}
/**
* Call [MainActivity.configureToolbarForDefineLinearProgramProblemCreation]
* to configure Toolbar for this fragment
*/
private fun setUpToolbar() {
if (activity is MainActivity) {
(activity as MainActivity).configureToolbarForFragmentB()
}
}
}
nav_graph.xml
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_graph"
app:startDestination="@id/fragmentA">
<fragment
android:id="@+id/fragmentB"
android:name="com.example.linearprogramming.framework.view.fragment.FragmentB"
android:label="FragmentB"
tools:layout="@layout/fragment_b">
<!--
<action
android:id="@+id/go_back_to_fragmentA"
app:popUpTo="@+id/fragmentA"/>
-->
</fragment>
<fragment
android:id="@+id/fragmentA"
android:name="com.example.linearprogramming.framework.view.fragment.FragmentA"
android:label="FragmentA"
tools:layout="@layout/fragment_a">
<action
android:id="@+id/action_from_fragment_a_to_fragment_b"
app:destination="@id/fragment" />
</fragment>
</navigation>
如果需要,我提供工具栏代码,现在它只是一个片段,它具有来自 MainActivity 的 Mainviemodel 作为视图模型,以便观察 ToolbarModel 更改以了解什么 ToolbarFragment 及其配置。 (例如)
我的 ToolbarFragment 更新了它在 FragmentB 上的配置,成功地为经典的 BackButton 添加了一个 ImageButton。
binding.backButton.setOnClickListener {
requireActivity().onBackPressedDispatcher.onBackPressed()
}
它成功地调用了 FragmentB 的回调
requireActivity().onBackPressedDispatcher.addCallback {
Toast.makeText(requireContext(), "Back from Define Problem fragment", Toast.LENGTH_SHORT).show()
// All the commented code below isn't working at all
//navController.popBackStack()
//navController.navigateUp()
//navController.navigate(R.id.go_back_to_createLinearProgramProblemFragment)
//navController.popBackStack(R.id.createLinearProgramProblemFragment, false)
}
正如直接在代码中通过评论所述,这些方法都没有像我在 StackOverflow 上阅读过的其他类似帖子那样按预期工作。
编辑:
我忘记添加主要活动 .xml 和(按原样)当前的 toolbarFragment(将来我计划添加具有不同功能的新工具栏)。我对片段没有深入的了解,但与在同一活动中显示 2 个片段以及使用破坏整个 BackPress 行为的导航有关。
activity_main.xml
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".framework.view.activity.MainActivity">
<FrameLayout
android:id="@+id/toolbar_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="56dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:name="com.example.linearprogramming.framework.view.fragment.BaseToolbarFragment"
tools:layout="@layout/fragment_base_toolbar" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_container_view"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar_container"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph"
tools:layout="@layout/fragment_create_linear_program_problem" />
</androidx.constraintlayout.widget.ConstraintLayout>
BaseToolbarFragment:
@ExperimentalCoroutinesApi
class BaseToolbarFragment : Fragment(R.layout.fragment_base_toolbar) {
private val binding: FragmentBaseToolbarBinding by viewBinding(FragmentBaseToolbarBinding::bind)
private val viewmodel: MainViewmodel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
observeData()
setUpViews()
}
private fun observeData() {
viewmodel.toolbar.observe(viewLifecycleOwner) {
if (it is ToolbarModel.BaseToolbar) {
binding.title.text = it.title.value
binding.backButton.visibility =
if (it.hasBackButton.value == true) View.VISIBLE
else View.GONE
}
}
}
private fun setUpViews() {
binding.backButton.setOnClickListener {
requireActivity().onBackPressedDispatcher.onBackPressed()
}
}
}
查看模型A:
@ExperimentalCoroutinesApi
@HiltViewModel
class ViewmodelA @Inject constructor(
) : ViewModel() {
...
// Observable LiveData
...
private val __problemModel = MutableLiveData<Problem>()
val problemModel: LiveData<Problem>
// This updates problemModel to be observed in the fragments
// (A, B, C, D, ...)
fun fragmentACompleted() {
_problemModel.value = FragmentAProblem(
// values obtained from FragmentA Questionnaire
)
}
// TODO the problemModel generated from FragmentB is the
// continuation of FragmentA Questionnaire
fun fragmentBCompleted() {
_problemModel.value = FragmentBProblem(
// values obtained from fragmentB Questionnaire
)
}
}