I'm still new to using Navigation Components, and I am struggling to get the fragments to switch when clicking on a simple button. This is fairly simple code, but I can't for the life of me get this to work. Any help would be appreciated.
Note: I am using SafeArgs
. Relevant code is displayed below:
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<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=".MainActivity">
<androidx.fragment.app.FragmentContainerView
android:id="@ id/fragmentContainerView"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/test_nav"/>
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivty.kt
package com.example.testingnavigationcomponent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.navigation.NavController
import androidx.navigation.findNavController
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.setupActionBarWithNavController
import com.example.testingnavigationcomponent.databinding.FragmentFirstBinding
class MainActivity : AppCompatActivity() {
private lateinit var navController: NavController
// private lateinit var binding: FragmentFirstBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navHostFragment = supportFragmentManager.findFragmentById(R.id.fragmentContainerView) as NavHostFragment
navController = navHostFragment.navController
setupActionBarWithNavController(navController)
}
override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp() || super.onSupportNavigateUp()
}
}
firstFragment.kt
package com.example.testingnavigationcomponent
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputBinding
import androidx.navigation.NavController
import androidx.navigation.fragment.findNavController
import com.example.testingnavigationcomponent.databinding.FragmentFirstBinding
class firstFragment : Fragment() {
private lateinit var binding: FragmentFirstBinding
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_first, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding = FragmentFirstBinding.inflate(layoutInflater)
binding.btnAccept.setOnClickListener {
val age = binding.etEnterAge.text.toString().toInt()
val name = binding.etEditName.text.toString()
val action = firstFragmentDirections.actionFirstFragmentToSecondFragment()
view.findNavController().navigate(action) }
}
}
test_nav.xml
<?xml version="1.0" encoding="utf-8"?>
<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/test_nav"
app:startDestination="@id/firstFragment">
<fragment
android:id="@ id/firstFragment"
android:name="com.example.testingnavigationcomponent.firstFragment"
android:label="fragment_first"
tools:layout="@layout/fragment_first" >
<action
android:id="@ id/action_firstFragment_to_secondFragment"
app:destination="@id/secondFragment" />
</fragment>
<fragment
android:id="@ id/secondFragment"
android:name="com.example.testingnavigationcomponent.secondFragment"
android:label="fragment_second"
tools:layout="@layout/fragment_second" >
<action
android:id="@ id/action_secondFragment_to_firstFragment"
app:destination="@id/firstFragment" />
</fragment>
</navigation>
CodePudding user response:
The problem isn't related to navigation components; as in the firstFragment
you've a different layout than that is displayed on the screen.
The following line of code, inflates a brand new layout; because already the fragment layout has been created & returned from onCreateView()
lifecycle method.
binding = FragmentFirstBinding.inflate(layoutInflater)
Therefore, the binding.btnAccept
is a different view than requireView().findViewById(R.id.btn_accept)
; the first is not related to the shown fragment to the user; while the latter is the one shown on the screen.
By using return inflater.inflate(R.layout.fragment_first, container, false)
you're not actually using ViewBinding
/DataBinding
; this is the transitional inflation without using the generated binding class.
So, to fix this, you've to use ViewBinding/DataBinding in onCreateView
by initializing the binding
object in the onCreateView
to inflate the layout that will be returned by this method not a brand new layout:
class firstFragment : Fragment() {
private lateinit var binding: FragmentFirstBinding
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
binding = FragmentFirstBinding.inflate(layoutInflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding.btnAccept.setOnClickListener {
val age = binding.etEnterAge.text.toString().toInt()
val name = binding.etEditName.text.toString()
val action = firstFragmentDirections.actionFirstFragmentToSecondFragment()
view.findNavController().navigate(action) }
}
}