61

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.

3
  • I'm afraid you'll have to handle it mannually Commented Jun 4, 2018 at 21:49
  • What are you trying to do? Commented Jun 4, 2018 at 22:04
  • @ianhanniballake My host Activity need references to communicate with its fragments Commented Jun 4, 2018 at 22:10

10 Answers 10

65

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"

Sign up to request clarification or add additional context in comments.

3 Comments

in my case ?.childFragmentManager?.fragments?.last() gives me current fragment
Wow, hacky answers. childFragmentManager.findFragmentById exists.
How is childFragmentManager.findFragmentById gonna help if they want to check what is the current fragment?
50

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.

6 Comments

All links show how to communicate between fragments. But how we can call fragment method from activity if navigation does not provide any mechanism for getting the implementation of the current destination?
@Sever - see this issue and how to use getPrimaryNavigationFragment() to retrieve the current Fragment.
Big thanks. It works. val navHostFragment = supportFragmentManager.primaryNavigationFragment as NavHostFragment val yourFragment = currentFragmentClassName.childFragmentManager.primaryNavigationFragment as YourFragment
@ianhanniballake: Sorry, I didn't understand is there a way to get the foreground fragment by means of getPrimaryNavigationFragment() now or they are going to provide such opportunity in the future only!?
Well, to share ViewModel between two fragments, one of them must be its owner. And the other one must find that owner in order to fetch that ViewModel. So… just never use Navigation, it's retarded.
|
30

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)

1 Comment

You can remove as? NavHostFragment and make the if-statement if (navHostFragment is NavHostFragment)
14

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

Comments

9

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)

1 Comment

You can squish this down to inline fun <reified T : Fragment> FragmentManager.findFragmentByClass(): T? = fragments.firstOrNull { it is T } as T?
5

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);
}

Comments

3

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.

Comments

3

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);

1 Comment

Small correction for kotlin: it should be primaryNavigationFragment at the end, i.e. without a function call
0

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
}

Comments

0

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() }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.