Компонент архітектури навігації Android - Отримайте поточний видимий фрагмент


95

До того, як спробувати компонент навігації, я використовував ручні транзакції фрагментів і використовував тег фрагмента для отримання поточного фрагмента.

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)

повертає a, NavHostFragmentі я хочу отримати показаний "MyFragment".

Дякую.


4
Немає можливості отримати поточний фрагмент stackoverflow.com/a/50689587/1268507 Чого ви намагаєтесь досягти? можливо, є якесь інше рішення для цього.
Алекс

З мого фрагмента мені потрібно було розпочати намір камери, сфотографувати та використовувати зроблене зображення. Для цього я розпочав діяльність і чекав результату в onActivityResultосновній своїй діяльності. Оскільки я не зміг отримати фрагмент, я просто перемістив усе це у сам фрагмент і, здається, працює.
Алін

Ви хочете виконати операцію над цим об'єктом фрагмента. Або просто ви хочете, який фрагмент показано?
Ranjan

Відповіді:


101

Наразі мені вдалося знайти спосіб, який полягає в наступному:

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

Якщо ви, звичайно, знаєте, це перший фрагмент. Я все ще розслідую спосіб без цього. Я згоден, що це не найкращий спосіб, але наразі це має бути чимось.


6
Чудово бачити робоче рішення. Я відмовився від цього і почав використовувати спільний viewModel між активністю та фрагментом, Це означає, що я можу передавати події між ними без необхідності зворотних дзвінків або прямого доступу.
Алін

Як зазначено у цій проблемі, повідомляючи про помилку Issetracker.google.com/issues/119800853, це не є правильним методом для цього. Будь ласка, подумайте про зміну правильної відповіді!
Ніколас П'єтрі

2
Як зазначалося, це не рішення, це обхідний шлях, поки не буде знайдено належного рішення.
Андрій Тоадер

1
Це працює, велике спасибі. Я спробував getPrimaryNavigationFragment (), як запропоновано в програмі відстеження помилок, але це не робить роботу.
Джеррі Ху

1
На основі цього рішення я створив функцію kotlin, яка може перевірити, чи є поточний фрагмент певного типу (замість ідентифікатора): navHostFragment!!.childFragmentManager.fragments.any { it.javaClass == fragmentClass && it.isVisible }працює досить схоже. Сподіваюся, це комусь допоможе!
P Kuijpers

49

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

navController.currentDestination?.getId()

Він повертає ідентифікатор у навігаційному файлі. Сподіваюся, це допомагає.


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

7
я очікував, що повернутий ідентифікатор відповідає ідентифікатору в nav xml, але він не відповідає, тому іншими словами ви не можете перевірити navController.getCurrentDestination (). getId () == R.id.myfrag
Leres Aldtai

4
@LeresAldtai Він відповідає ідентифікатору фрагмента у навігаційному файлі.
slhddn

1
Дякую @slhddn. Ваш коментар мені допоміг і зміг отримати ідентифікатор із фрагмента та зробити цю роботу. Тут додано відповідь із моїм кодом на випадок, якщо це буде корисно комусь іншому. Ура.
Francislainy Campos

32

Ви повинні шукати властивість childFragmentManager primaryNavigationFragment вашого фрагмента навігації, саме тут посилається на поточний відображений фрагмент.

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

2
Цей метод тепер задокументований у фрагменті Manager issueetracker.google.com/issues/119800853
Ніколас П’єтрі

здається, це трохи проблема. обробляю навігацію, я повинен налаштувати панель дій, щоб отримувати виклик onSupportNaigateUp ()
filthy_wizard

18

Це здається найчистішим рішенням. Оновлення: функцію розширення змінено на властивість. Дякую Ігоре Войді .

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

2
Приємно - це я б змінив - визначив розширення як властивість FragmentManager.currentNavigationFragment(а не як функцію)
Ігор Войда

16

Використання 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);

    }

8

це може бути пізно, але для тих, хто все ще шукає

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

тоді ви можете зробити щось на зразок

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

зауважте, що це для kotlin, код Java повинен бути майже однаковим



5
navController().currentDestination?.id

дасть вам ваш поточний Fragmentідентифікатор, де

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

цей ідентифікатор - це ідентифікатор, який ви вказали у своєму Navigation Graph XMLфайлі під fragmentтегом. Ви також можете порівняти, currentDestination.labelякщо хочете.


4

Знайдено робоче рішення.

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

3

За допомогою Activity, що використовує NavHostFragment, ви можете скористатися наведеним нижче кодом для отримання екземпляра Active Fragment.

val fragmentInstance = myNavHostFragment?.childFragmentManager?.primaryNavigationFragment

3

Моя вимога - отримати поточний видимий фрагмент з активності батьків, моє рішення -

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

Це може допомогти комусь із такою ж проблемою.


отримав Котлін Ланг?
ІгорГанапольський

1
Для мене це ідеально працює, не впевнений, що з іншими відповідями? Можливо, це раніше не спрацьовувало, але зараз це працює як шарм.
Весс

2

Відповідно до відповіді та коментаря @slhddn, ось як я його використовую та отримую ідентифікатор фрагмента з файлу 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

Ви можете getChildFragmentManager()зателефонувати зателефонувавши за допомогою

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

1

Зробити:

• у якійсь кнопці у вашому фрагменті:

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

• У вашій діяльності onBackPress ()

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

1

Якщо вам потрібен екземпляр вашого фрагмента, ви можете піти з:

(Діяльність) => supportFragmentManager.fragments.first().childFragmentManager.fragments.first()

Це дуже потворно, але звідти ви можете перевірити, чи це екземпляр фрагмента, з яким ви хочете пограти


1

На 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;
}

1
Ласкаво просимо до SO! Будь ласка, перед тим, як розмістити свою відповідь, перевірте її ... Код незрозумілий. Інше питання: якщо ви просто приносите код, поясніть трохи своє рішення, максимум, коли є ще 16 відповідей, тому вам слід пояснити плюси вашої відповіді.
Девід Гарсія Бодего,

1

Першим фрагментом у фрагменті navhost є поточний фрагмент

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

Чи не могли б Ви докладніше розказати свою відповідь? Можливо, пояснивши свій код.
двадцять

Відповіді лише на коди не рекомендується. Надайте пояснення, чому і як ваша відповідь вирішує проблему.
Ігор Ф.

1

Спочатку ви отримуєте поточний фрагмент за ідентифікатором, потім ви можете знайти фрагмент, залежно від цього ідентифікатора.

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

0

Я об'єднав Андрій Toaders відповідь і Mukul Bhardwajs відповідь з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.


0

Мені потрібно зробити це, щоб налаштувати нижній перегляд навігації, і я зробив це таким чином:

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

0

Найкращий спосіб досягти цього - зареєструвати зворотний виклик життєвого циклу фрагмента .

Відповідно до вихідного запитання, якщо ваше 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
        }
    }
)

Інші методи зворотного дзвінка можуть бути замінені відповідно до ваших вимог.


0

Це рішення буде працювати в більшості ситуацій Спробуйте:

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

або це:

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

0

Цей код працює від мене

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

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


0

це може бути пізно, але може допомогти комусь пізніше.

якщо ви використовуєте вкладені навігації, ви можете отримати ідентифікатор, подібний до цього в Kotlin

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

і це один із фрагментів у фрагменті_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"){}

0

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

виконайте такі дії:

  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. Впровадьте свій слухач у фрагмент / активність хоста

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

І це все, що потрібно для виконання роботи. Хоп це допомогти комусь


0

Найкращий спосіб, який я міг зрозуміти за допомогою одного фрагмента, Kotlin, Використання навігації:

У файл макета додайте тег: "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()

0
val fragment: YourFragment? = yourNavHost.findFragment()

Примітка: findFragmentфункція розширення знаходиться в androidx.fragment.appпакеті, і це загальна функція


-2

У своїй діяльності визначте фрагмент об'єкта. І з кожного фрагмента викличте цей метод, передаючи фрагмент-об'єкт, як, отже, статичний об'єкт фрагмента буде замінюватися поточним що показує фрагментом кожного разу.

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

Ви також можете встановити зворотний виклик від фрагмента до активності для кращого підходу та передачі поточного об’єкта фрагмента.


2
Спасибі за ваш час. Я шукав щось для використання з самого компонента Навігація.
Алін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.