How I can retrieve current fragment in NavHostFragment?
AndroidAndroid FragmentsKotlinAndroid Architecture-ComponentsAndroid Architecture-NavigationAndroid Problem Overview
I tried to find a method in the new Navigation components but I didn't find anything about that.
I have the current destination with :
mainHostFragment.findNavController().currentDestination
But I can't get any reference to the displayed fragment.
Android Solutions
Solution 1 - Android
Reference to the displayed fragment (AndroidX):
java
public Fragment getForegroundFragment(){
Fragment navHostFragment = getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);
return navHostFragment == null ? null : navHostFragment.getChildFragmentManager().getFragments().get(0);
}
kotlin
val navHostFragment: Fragment? =
supportFragmentManager.findFragmentById(R.id.nav_host_fragment)
navHostFragment?.childFragmentManager?.fragments?.get(0)
Here nav_host_fragment
is an ID
of the fragment
tag in your activity_main.xml
with android:name="androidx.navigation.fragment.NavHostFragment"
Solution 2 - Android
Navigation does not provide any mechanism for getting the implementation (i.e., the Fragment itself) of the current destination.
As per the Creating event callbacks to the activity, you should either communicate with your Fragment by
-
Having the Fragment register a callback in its
onAttach
method, casting your Activity to an instance of an interface you provide -
Use a shared
ViewModel
that your Activity and Fragment use to communicate.
Solution 3 - Android
You can do something like this:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
val navHostFragment = supportFragmentManager.fragments.first() as? NavHostFragment
if(navHostFragment != null) {
val childFragments = navHostFragment.childFragmentManager.fragments
childFragments.forEach { fragment ->
fragment.onActivityResult(requestCode, resultCode, data)
}
}
}
But for more advanced communication Listeners with callback methods registered in Fragment.onAttach() (Fragment -> Activity rather one direction communication
) and SharedViewModel (bidirectional
, important to have ViewModelProviders, and Lifecycle owner that is scoped to getActivity()
rather)
Solution 4 - Android
Based on other answers
Fragment navHostFragment = getSupportFragmentManager().getPrimaryNavigationFragment();
Fragment fragment = navHostFragment.getChildFragmentManager().getFragments().get(0);
((Your Fragment Class) fragment).(public method inside the fragment)
worked for me
Solution 5 - Android
Using Michal's answer I wrote this extension function for testing:
@Suppress("UNCHECKED_CAST")
fun <F : Fragment> AppCompatActivity.getFragment(fragmentClass: Class<F>): F? {
val navHostFragment = this.supportFragmentManager.fragments.first() as NavHostFragment
navHostFragment.childFragmentManager.fragments.forEach {
if (fragmentClass.isAssignableFrom(it.javaClass)) {
return it as F
}
}
return null
}
Used like this:
val myFragment = activity.getFragment(MyFragment::class.java)
Solution 6 - Android
You can do like this:
@Override
public void onBackPressed() {
super.onBackPressed();
NavController navController = Navigation.findNavController(this, R.id.fragment);
int id=navController.getCurrentDestination().getId();
if(id == R.id.startGameFragment ){
selectedPosition(0);
} else if(id == R.id.gameFragment ){
selectedPosition(1);
} else if(id == R.id.endGameFragment){
selectedPosition(2);
}
}
private void selectedPosition(int pos){
for (int i = 0; i >=nav_view.getMenu().size(); i++) {
nav_view.getMenu().getItem(pos).setChecked(false);
}
nav_view.getMenu().getItem(pos).setChecked(true);
}
Solution 7 - Android
From an Activity which has NavHostFragment, below code snippet can be used to retrieve the instance of the Active Fragment.
kotlin
val currentFragment = mNavHostFragment?.childFragmentManager?.primaryNavigationFragment
java
Fragment navHostFragment = getSupportFragmentManager().getPrimaryNavigationFragment();
Fragment currentFragment = navHostFragment.getChildFragmentManager().getFragments().get(0);
Solution 8 - Android
I post my complete answer with androidx. Care : in my case i needed to retrieve one of the child fragment (could not be the first).
In MainActivity you should have something like :
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
DrawerLayout drawer = findViewById(R.id.drawer_layout);
NavigationView navigationView = findViewById(R.id.nav_view);
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
mAppBarConfiguration = new AppBarConfiguration.Builder(
R.id.mytest, R.id.nav_help)
.setDrawerLayout(drawer)
.build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
NavigationUI.setupWithNavController(navigationView, navController);
...
Then you have to create a method to retrieve the good fragment.
private MyTestFragment getMyTestFragment(){
MyTestFragment resultFragment = null;
Fragment navHostFragment = getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);
if(navHostFragment != null && navHostFragment.getChildFragmentManager() != null) {
List<Fragment> fragmentList = navHostFragment.getChildFragmentManager().getFragments();
for (Fragment fragment : fragmentList) {
if (fragment instanceof MyTest) {
resultFragment = (MyTest) fragment;
break;
}
}
}
return resultFragment;
}
Finally you get it.
Solution 9 - Android
Really the best and correct way to handle this is to use an interface. View models should really be used for passing data between the activity and fragments. Here is how I solved this problem:
Create interface
interface NavigationInterface {
fun referenceCourseListFragment(fragment: CourseListFragment)
fun referenceCouseDetailFragment(fragment: CourseDetailInfoFragment)
}
Make sure activity implements interface
class NotesActivity : AppCompatActivity(), NavigationInterface {}
Make sure to create lateinit var for each fragment you need to reference and then
private lateinit var courseListFragment: CourseListFragment
private lateinit var courseDetailInfoFragment: CourseDetailInfoFragment
Now in each fragment in the onCreateView method, make sure to create the interface listener and passback the fragment with the interface
private lateinit var navigationInterface: NavigationInterface
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
//establish interface communication
activity?.let {
instantiateNavigationInterface(it)
}
// Inflate the layout for this fragment
fragmentView = inflater.inflate(R.layout.fragment_course_list, container,
false)
navigationInterface.referenceCourseListFragment(this)
return fragmentView
}
Now back in the activity, you should be able to instantiate your fragment objects for reference via interface callback
override fun referenceCourseListFragment(fragment: CourseListFragment) {
courseListFragment = fragment
}
override fun referenceCouseDetailFragment(fragment: CourseDetailInfoFragment)
{
courseDetailInfoFragment = fragment
}
Solution 10 - Android
I think there is chance of exceptions, so you can use interface callback this is solid way to communicate from one end to other end only pass reference check below code. > > Fragment A(){ interface ClickPostItemListener { > fun onClicked(position: Int) > } > > val itemListener = object : ClickPostItemListener{ > override fun onClicked(position: Int) { > postBackNForth(position) > } > > } } Fragment B(clickItem:ClickPostItemListener ){ clickItem() }