Компонент архитектуры навигации Android - получить текущий видимый фрагмент

Перед тем, как попробовать компонент Navigation, я вручную выполнял транзакции фрагментов и использовал тег фрагмента, чтобы получить текущий фрагмент.

val fragment:MyFragment = supportFragmentManager.findFragmentByTag(tag):MyFragment

Теперь в моем основном макете деятельности у меня есть что-то вроде:

<fragment
        android:layout_width = "match_parent"
        android:layout_height = "match_parent"
        android:id = "@+id/nav_host"
        app:navGraph= "@navigation/nav_item"
        android:name = "androidx.navigation.fragment.NavHostFragment"
        app:defaultNavHost= "true"
        />

Как я могу получить текущий отображаемый фрагмент с помощью компонента навигации? Делает

supportFragmentManager.findFragmentById(R.id.nav_host)

возвращает NavHostFragment, и я хочу получить показанный мной MyFragment.

Спасибо.

Невозможно получить текущий фрагмент. stackoverflow.com/a/50689587/1268507 Чего вы пытаетесь достичь? возможно, есть какое-то другое решение.

Alex 18.07.2018 07:27

Мне нужно было из моего фрагмента запустить замысел камеры, сделать снимок и использовать полученное изображение. Для этого я начал действие и ждал результата в onActivityResult моей основной деятельности. Поскольку получить фрагмент не удалось, я просто переместил все это в сам фрагмент и вроде работает.

Alin 18.07.2018 10:14

Вы хотите выполнить операцию с этим объектом-фрагментом. Или просто хотите, какой фрагмент показан?

Ranjan 30.07.2018 10:07
118
3
76 762
31
Перейти к ответу Данный вопрос помечен как решенный

Ответы 31

Можете ли вы проверить с помощью getChildFragmentManager(), позвоните с

NavHostFragment fragment = supportFragmentManager.findFragmentById(R.id.nav_host);
MyFragment frag = (MyFragment) fragment.getChildFragmentManager().findFragmentByTag(tag);

Я не могу найти способ получить текущий экземпляр фрагмента. Однако вы можете получить идентификатор последнего добавленного фрагмента, используя приведенный ниже код.

navController.currentDestination?.getId()

Он возвращает идентификатор в файле навигации. Надеюсь это поможет.

Спасибо за ваш ответ. Мне нужно было получить фрагмент, чтобы я мог с ним взаимодействовать. Поскольку navController не может его вернуть, я закончил использовать общую модель просмотра, чтобы разговаривать между активностью и ее фрагментами.

Alin 28.07.2018 22:25

Я ожидал, что возвращаемый идентификатор соответствует идентификатору в nav xml, но это не так, поэтому, другими словами, вы не можете проверить navController.getCurrentDestination (). getId () == R.id.myfrag

Leres Aldtai 07.02.2019 17:52

@LeresAldtai Соответствует идентификатору фрагмента в файле навигации.

slhddn 07.02.2019 23:47

Спасибо @slhddn. Ваш комментарий помог мне и смог получить идентификатор из фрагмента и выполнить эту работу. Добавил сюда ответ с моим кодом на случай, если это будет полезно кому-то другому. Ваше здоровье.

Francislainy Campos 15.07.2020 07:28

Я все еще не понимал, как мы можем сопоставить, если мы находимся на нужном фрагменте. findNavController (). Значение currentDestination.id - 2131296642, значение R.id.myFragment - 1000319. Хотя идентификаторы не имеют значения, отправьте его для понимания. В основном я хочу проверить и сопоставить, на каком фрагменте я нахожусь, из BaseFragment для настройки общего обратного вызова для всех фрагментов.

akhilsreekar 21.06.2021 00:50

В своей деятельности определите объект-фрагмент. И из каждого фрагмента вызовите этот метод, передав объект фрагмента, например Таким образом, статический объект фрагмента будет заменяться текущим отображаемым фрагментом каждый раз.

//method in MainActivity
private static Fragment fragment;

public void setFragment(Fragment fragment){
this.fragment = fragment;
}

//And from your fragment class, you can call
((<yourActivity in which fragment present>)getActivity()).setFragment(<your fragment object>);

//if you want to set it directly;
 ((<yourActivity in which fragment present>)getActivity()).fragment=<your fragment object>;

Вы также можете установить обратный вызов от фрагмента к действию для лучшего подхода и передать текущий объект фрагмента.

Спасибо за ваше время. Я искал что-то, что можно было бы использовать в самом навигационном компоненте.

Alin 30.07.2018 22:54
Ответ принят как подходящий

На данный момент мне удалось найти способ, и он выглядит следующим образом:

NavHostFragment navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host);
navHostFragment.getChildFragmentManager().getFragments().get(0);

Если вы, конечно, знаете, что это первый фрагмент. Я все еще ищу способ без этого. Я согласен, что это не лучший способ, но на данный момент это должно быть что-то особенное.

Приятно видеть рабочее решение. Я отказался от этого и начал использовать общую модель представления между активностью и фрагментом. Это означает, что я могу передавать события между ними без обратных вызовов или прямого доступа.

Alin 07.08.2018 21:07

Как упоминалось в этом сообщении об ошибке Issuesetracker.google.com/issues/119800853, это неправильный метод. Пожалуйста, подумайте об изменении правильного ответа!

Nicolas Pietri 02.02.2019 08:38

Как уже упоминалось, это не решение, это обходной путь, пока не будет найдено правильное решение.

Andrei Toader 02.02.2019 10:15

В моем случае верхний фрагмент не по нулевому индексу, а по последнему индексу

Leo Droidcoder 26.02.2019 18:51

В общем, этого делать не нужно. Если вам нужно это сделать, в вашей архитектуре есть изъян. По крайней мере, если совместить с навигационным графиком.

Andrei Toader 28.02.2019 08:31

Андрей: в моем случае у меня есть ProgressBar (ожидающий счетчик) в основном действии, и теперь я хочу добавить кнопку отмены на счетчике. Кнопка отмены в основном действии должна отправить сообщение фрагменту для отмены операции. Должен ли я отказаться от этого подхода и вместо этого создать представление ProgressBar внутри каждого фрагмента? Мне кажется, что «несовершенная архитектура» может быть другим словом, означающим «это сложно сделать с этой (несовершенной?) Платформой».

Rowan Gontier 03.05.2019 03:09

Добавьте это как отдельный вопрос и свяжите здесь.

Andrei Toader 16.05.2019 11:10

java.lang.IndexOutOfBoundsException: Индекс: 3

Joseph Wambura 04.09.2019 12:38

В документации говорится: «Порядок фрагментов в списке - это порядок, в котором они были добавлены или прикреплены». Я сделал это, и это должно быть немного безопаснее. Но все равно не лучшим образом .. navHostFragment.childFragmentManager.fragments.firstOrNull { it.isVisible }

Joshua King 20.09.2019 18:19

Это работает, большое спасибо. Я попробовал getPrimaryNavigationFragment (), как было предложено в системе отслеживания ошибок, но это не помогло.

Jerry Hu 23.06.2020 04:21

getPrimaryNavigationFragment () не может найти navHostFragment, когда вы программно устанавливаете график и находитесь в startDestination. findViewById не потерпит неудачу в этой ситуации.

straya 10.08.2020 15:17

На основе этого решения я создал функцию kotlin, которая может проверять, имеет ли текущий фрагмент определенный тип (вместо идентификатора): navHostFragment!!.childFragmentManager.fragments.any { it.javaClass == fragmentClass && it.isVisible } Работает очень похоже. Надеюсь, это кому-нибудь поможет!

P Kuijpers 01.09.2020 18:43

Вы должны посмотреть свойство childFragmentManager primaryNavigationFragment вашего навигационного фрагмента, именно здесь есть ссылка на текущий отображаемый фрагмент.

val navHost = supportFragmentManager.findFragmentById(R.id.main_nav_fragment)
    navHost?.let { navFragment ->
        navFragment.childFragmentManager.primaryNavigationFragment?.let {fragment->
                //DO YOUR STUFF
        }
    }
}

Этот метод теперь задокументирован в fragmentManager Issuesetracker.google.com/issues/119800853.

Nicolas Pietri 02.02.2019 08:39

это кажется небольшой проблемой. обработка навигации вверх, мне нужно настроить панель действий, чтобы получить вызов onSupportNaigateUp ()

filthy_wizard 25.02.2019 16:16

Нашел рабочее решение.

private fun getCurrentFragment(): Fragment? {
    val currentNavHost = supportFragmentManager.findFragmentById(R.id.nav_host_id)
    val currentFragmentClassName = ((this as NavHost).navController.currentDestination as FragmentNavigator.Destination).className
    return currentNavHost?.childFragmentManager?.fragments?.filterNotNull()?.find {
        it.javaClass.name == currentFragmentClassName
    }
}

Использовать addOnDestinationChangedListener в деятельности с NavHostFragment

Для Java

 NavController navController= Navigation.findNavController(MainActivity.this,R.id.your_nav_host);

        navController.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() {
            @Override
            public void onDestinationChanged(@NonNull NavController controller, @NonNull NavDestination destination, @Nullable Bundle arguments) {
                Log.e(TAG, "onDestinationChanged: "+destination.getLabel());
            }
        });

Для Котлина

var navController :NavController=Navigation.findNavController(this, R.id.nav_host_fragment)
    navController.addOnDestinationChangedListener { controller, destination, arguments ->
        Log.e(TAG, "onDestinationChanged: "+destination.label);

    }

это может быть поздно, но для тех, кто все еще ищет

val currentFragment = NavHostFragment.findNavController(nav_host_fragment).currentDestination?.id

тогда вы можете сделать что-то вроде

if (currentFragment == R.id.myFragment){
     //do something
}

обратите внимание, что это для котлина, Java-код должен быть почти таким же

Вот код Java, stackoverflow.com/a/60539704/4291272

Faisal Shaikh 05.03.2020 07:54

Прекрасно работает. Спасибо.

Victor Okech 24.01.2021 23:19

Я объединил Андрей Тоадерс отвечает и Ответ Мукула Бхардваджа с OnBackStackChangedListener.

В качестве атрибута:

private FooFragment fragment;

В моем onCreate

NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host);
FragmentManager navHostManager = Objects.requireNonNull(navHostFragment).getChildFragmentManager();
FragmentManager.OnBackStackChangedListener listener = () -> {
        List<Fragment> frags = navHostManager.getFragments();
        if (!frags.isEmpty()) {
            fragment = (FooFragment) frags.get(0);
        }
};
navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
        if (destination.getId()==R.id.FooFragment){
            navHostManager.addOnBackStackChangedListener(listener);
        } else {
            navHostManager.removeOnBackStackChangedListener(listener);
           fragment = null;
        }
});

Таким образом я могу получить фрагмент, который будет отображаться позже. Можно даже выполнить цикл через frags и проверить несколько фрагментов с помощью instanceof.

Мне нужно сделать это, чтобы настроить нижний вид навигации, и я сделал это следующим образом:

val navController = Navigation.findNavController(requireActivity(), R.id.nav_tabs_home)
        val bottomNavBar: BottomNavigationView = nav_content_view
        NavigationUI.setupWithNavController(bottomNavBar, navController)

Сначала вы получаете текущий фрагмент по идентификатору, затем вы можете найти фрагмент в зависимости от этого идентификатора.

int id = navController.getCurrentDestination().getId();
Fragment fragment = getSupportFragmentManager().findFragmentById(id);

Это кажется самым чистым решением. Обновлять: Функция расширения изменена на свойство. Спасибо Игорь Войда.

1. Создайте следующее расширение

import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager

val FragmentManager.currentNavigationFragment: Fragment?
    get() = primaryNavigationFragment?.childFragmentManager?.fragments?.first()

2. В Activity с NavHostFragment используйте это так:

val currentFragment = supportFragmentManager.currentNavigationFragment

Хорошо - я бы это изменил - я бы определил расширение как свойство FragmentManager.currentNavigationFragment (а не как функцию)

Igor 03.04.2020 15:00

Это рабочее решение. Спасибо!

Parth Patel 29.04.2021 16:46

Лучший способ добиться этого - зарегистрировать обратный вызов жизненного цикла фрагмента.

Согласно исходному вопросу, если ваш NavHostFragment определяется как ...

<fragment
        android:layout_width = "match_parent"
        android:layout_height = "match_parent"
        android:id = "@+id/nav_host"
        app:navGraph= "@navigation/nav_item"
        android:name = "androidx.navigation.fragment.NavHostFragment"
        app:defaultNavHost= "true"
        />

Тогда в вашем основном занятии onCreate(..) ...

nav_host.childFragmentManager.registerFragmentLifecycleCallbacks(
    object:FragmentManager.FragmentLifecycleCallbacks() {
        override fun onFragmentViewCreated(
            fm: FragmentManager, f: Fragment, v: View,
            savedInstanceState: Bundle?
        ) {
            // callback method always called whenever a
            // fragment is added, or, a back-press returns
            // to the start-destination
        }
    }
)

Другие методы обратного вызова могут быть переопределены в соответствии с вашими требованиями.

Делать :

• в какой-то кнопке в вашем фрагменте:

val navController = findNavController(this)
  navController.popBackStack()

• В вашей деятельности onBackPressed ()

override fun onBackPressed() {
        val navController = findNavController(R.id.nav_host_fragment)
        val id = navController.currentDestination?.getId()
        if (id == R.id.detailMovieFragment){
            navController.popBackStack()
            return
        }

        super.onBackPressed()
    } 

Если вам нужен экземпляр вашего фрагмента, вы можете использовать:

(Активность) => supportFragmentManager.fragments.first().childFragmentManager.fragments.first()

Это очень некрасиво, но оттуда вы можете проверить, является ли это экземпляром фрагмента, с которым вы хотите поиграть

На Androidx я перепробовал много способов найти нужный фрагмент из NavHostFragment. Наконец, я смог взломать его с помощью метода ниже

public Fragment getFunctionalFragment (String tag_name)
{
    NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);
    FragmentManager navHostManager = Objects.requireNonNull(navHostFragment).getChildFragmentManager();

    Fragment fragment=null;
    List fragment_list = navHostManager.getFragments();

    for (int i=0; i < fragment_list.size() ; i ++ )
    {
        if ( fragment_list.get(i) instanceof HomeFragment && tag_name.equals("frg_home")) {
            fragment = (HomeFragment) fragment_list.get(i);
            break;
        }
    }

    return fragment;
}

Добро пожаловать в SO! Пожалуйста, прежде чем размещать свой ответ, проверьте его ... Код не понятен. Другая проблема: если вы просто приводите код, немного объясните свое решение, максимум, когда есть еще 16 ответов, поэтому вы должны объяснить плюсы своего ответа.

David García Bodego 31.10.2019 08:52
navController().currentDestination?.id

предоставит вам текущий идентификатор Fragment, где

private fun navController() = Navigation.findNavController(this, R.id.navHostFragment)

этот идентификатор - это идентификатор, который вы указали в своем файле Navigation Graph XML в теге fragment. Вы также можете сравнить currentDestination.label, если хотите.

Из Activity, в котором используется NavHostFragment, вы можете использовать приведенный ниже код для получения экземпляра Active Fragment.

val fragmentInstance = myNavHostFragment?.childFragmentManager?.primaryNavigationFragment

Первый фрагмент во фрагменте navhost - это текущий фрагмент

Fragment navHostFragment = getSupportFragmentManager().getPrimaryNavigationFragment();
if (navHostFragment != null) 
{Fragment fragment = navHostFragment.getChildFragmentManager().getFragments().get(0);}

Не могли бы вы подробнее рассказать о своем ответе? Может быть, объяснив свой код.

Twenty 12.02.2020 01:20

Ответы только на код не приветствуются. Объясните, почему и как ваш ответ решает проблему.

Igor F. 12.02.2020 09:14

Это правильный ответ. Просто проверьте это. Сначала мы получаем первичный фрагмент навигации, который используется для выделения фрагментов, которые мы раздуваем при навигации. Во-вторых, мы получаем диспетчер дочерних фрагментов, который является частной записью, которую первичный фрагмент построил внутри для управления различными фрагментами, которые мы раздуваем. Наконец, все, что нам нужно сделать, это получить последний раздутый фрагмент. Проверьте работающий пример котлина: val pF = supportFragmentManager.primaryNavigationFragment val cF = pF !!. ChildFragmentManager.fragments.last () cF.updateMethod ()

Ramiro G.M. 22.02.2021 03:43

Этот код работает у меня

NavDestination current = NavHostFragment.findNavController(getSupportFragmentManager().getPrimaryNavigationFragment().getFragmentManager().getFragments().get(0)).getCurrentDestination();

switch (current.getId()) {
    case R.id.navigation_saved:
        // WRITE CODE
        break;
}

Это решение будет работать в большинстве ситуаций. Попробуй это:

val mainFragment = fragmentManager?.primaryNavigationFragment?.parentFragment?.parentFragment as MainFragment

или это:

val mainFragment = fragmentManager?.primaryNavigationFragment?.parentFragment as MainFragment

Это работает для меня

(navController.currentDestination as FragmentNavigator.Destination).className 

Он вернет canonicalName вашего фрагмента

Это похоже на отражение

IgorGanapolsky 14.07.2020 19:20

Мое требование - получить текущий видимый фрагмент из родительской активности, мое решение

 private LoginFragment getCurrentVisibleFragment() {
        NavHostFragment navHostFragment = (NavHostFragment)getSupportFragmentManager().getPrimaryNavigationFragment();
        FragmentManager fragmentManager = navHostFragment.getChildFragmentManager();
        Fragment loginFragment = fragmentManager.getPrimaryNavigationFragment();
        if (loginFragment instanceof LoginFragment){
            return (LoginFragment)loginFragment;
        }
        return null;
    }

Это может помочь кому-то с той же проблемой.

есть Kotlin lang?

IgorGanapolsky 14.07.2020 19:20

Это отлично работает для меня, не уверен, что с другими ответами? Может быть, раньше это не работало, но сейчас работает как шарм.

Wess 22.10.2020 14:26

это может быть поздно, но может помочь кому-то позже.

если вы используете вложенную навигацию, вы можете получить такой идентификатор в Котлине

val nav = Navigation.findNavController(requireActivity(), R.id.fragment_nav_host).currentDestination

и это один из фрагментов в fragment_nav_host.xml

<fragment
    android:id = "@+id/sampleFragment"
    android:name = "com.app.sample.SampleFragment"
    android:label = "SampleFragment"
    tools:layout = "@layout/fragment_sample" />

и вы можете проверить все, что вам нужно, вот так

if (nav.id == R.id.sampleFragment || nav.label == "SampleFragment"){}

В соответствии с ответом и комментарием @slhddn вот как я его использую и получаю фрагмент файла id из nav_graph.xml:

class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    setSupportActionBar(findViewById(R.id.toolbar))

    val navController = findNavController(this, R.id.nav_host_fragment)

    ivSettingsCog.setOnClickListener {

        if (navController.currentDestination?.id == R.id.updatesFragment) // Id as per set up on nav_graph.xml file
        {
            navController.navigate(R.id.action_updatesFragment_to_settingsFragment)
        }

    }

}

}

Наконец-то рабочее решение для тех из вас, кто все еще ищет На самом деле это очень просто, и вы можете добиться этого с помощью обратного вызова.

Следуй этим шагам:

  1. Создайте слушателя внутри фрагмента, экземпляр которого вы хотите получить из активности onCreate ()

    public class HomeFragment extends Fragment {
    
    private OnCreateFragment createLIstener;
    
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        createLIstener.onFragmentCreated(this);
        View root = inflater.inflate(R.layout.fragment_home, container, false);
        //findViews(root);
        return root;
    }
    
    @Override
    public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
        super.onCreateOptionsMenu(menu, inflater);
        inflater.inflate(R.menu.menu_main, menu);
    
    }
    
    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        if (item.getItemId()==R.id.action_show_filter){
            //do something
        }
        return super.onOptionsItemSelected(item);
    }
    
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        try {
            createLIstener = (OnCreateFragment) context;
        } catch (ClassCastException e) {
            throw new ClassCastException(context.toString() + " must implement OnArticleSelectedListener");
            }
        }
    
        public interface OnCreateFragment{
            void onFragmentCreated(Fragment f);
        }
    }
    
  2. Реализуйте свой слушатель в хосте Fragment / Activity

    public class MainActivity extends AppCompatActivity implements HomeFragment.OnCreateFragment {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            BottomNavigationView navView = findViewById(R.id.nav_view);
           // Passing each menu ID as a set of Ids because each
           // menu should be considered as top level destinations.
            AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
                    R.id.navigation_home,
                    R. ----)
            .build();
            NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
            NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
            NavigationUI.setupWithNavController(navView, navController);
        }
    
        @Override
        public void onFragmentCreated(Fragment f) {
            if (f!=null){
                H.debug("fragment created listener: "+f.getId());
                f.setHasOptionsMenu(true);
            }else {
                H.debug("fragment created listener: NULL");
            }
        }
    }
    

И это все, что вам нужно для работы. Хоп, это поможет кому-нибудь

Лучший способ, которым я мог разобраться, с использованием 1 фрагмента, Kotlin, Navigation:

В файл макета добавьте тег: "android: tag =" main_fragment_tag ""

<fragment
        android:layout_width = "match_parent"
        android:layout_height = "match_parent"
        android:id = "@+id/nav_host"
        app:navGraph= "@navigation/nav_item"
        android:tag = "main_fragment_tag"
        android:name = "androidx.navigation.fragment.NavHostFragment"
        app:defaultNavHost= "true"
        />

О вашей деятельности:

val navHostFragment = supportFragmentManager.findFragmentByTag("main_fragment_tag") as NavHostFragment
val mainFragment = navHostFragment.childFragmentManager.fragments[0] as MainFragment
mainFragment.mainFragmentMethod()
val fragment: YourFragment? = yourNavHost.findFragment()

Примечание. Функция расширения findFragment находится в пакете androidx.fragment.app и является общей функцией.

Используя компоненты навигации, я получил текущий отображаемый фрагмент с помощью этой строки кода:

val fragment = parentFragmentManager.primaryNavigationFragment

Вот мое решение для java

    NavHostFragment  navHostFragment= (NavHostFragment) getSupportFragmentManager().getFragments().get(0);
    MyFragment  fragment = (MyFragment) navHostFragment.getChildFragmentManager().getFragments().get(0);
    fragment.doSomething();

В рамках вашей деятельности, на которой размещен NavHostFragment, мы можем написать следующий фрагмент кода для получения экземпляра отображаемого в данный момент фрагмента.

Fragment fragment = myNavHostFragment.getChildFragmentManager().findFragmentById(R.id.navHostFragmentId)
if (fragment instanceOf MyFragment) {
  //Write your code here
}

Здесь R.id.navHostFragmentId - это resId для NavHostFragment в вашей деятельности xml.

Другие вопросы по теме