Home > Mobile >  Navigation Controller not Switching Fragments on Button Press
Navigation Controller not Switching Fragments on Button Press

Time:06-18

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)        }
    }
}
  • Related