Компонент архітектури навігації 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.