Komponent architektury nawigacji Android - pobierz aktualny widoczny fragment


105

Przed wypróbowaniem komponentu Nawigacja ręcznie wykonywałem transakcje fragmentaryczne i użyłem znacznika fragmentu, aby pobrać bieżący fragment.

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

Teraz w moim głównym układzie aktywności mam coś takiego:

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

Jak mogę odzyskać aktualnie wyświetlany fragment przez komponent nawigacji? Robić

supportFragmentManager.findFragmentById(R.id.nav_host)

zwraca a NavHostFragmenti chcę pobrać pokazany przeze mnie „MyFragment”.

Dziękuję Ci.


4
Nie ma możliwości odzyskania bieżącego fragmentu stackoverflow.com/a/50689587/1268507 Co próbujesz osiągnąć? może jest na to inne rozwiązanie.
— Alex

Potrzebowałem z mojego fragmentu, aby uruchomić zamiar kamery, zrobić zdjęcie i wykorzystać zrobione zdjęcie. Aby to zrobić, rozpocząłem działanie i czekałem na wynik w onActivityResultmojej głównej działalności. Ponieważ nie mogłem pobrać fragmentu, po prostu przeniosłem to wszystko do samego fragmentu i wydaje się, że działa.
— Alin

Czy chcesz wykonać operację na tym obiekcie fragmentu. A może po prostu chcesz, który fragment jest wyświetlany?
— Ranjan

Odpowiedzi:


106

Na razie udało mi się znaleźć sposób i wygląda to następująco:

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

Jeśli oczywiście wiesz, że to pierwszy fragment. Nadal szukam sposobu bez tego. Zgadzam się, że to nie jest najlepszy sposób, ale na razie to powinno być coś.


7
Wspaniale jest zobaczyć działające rozwiązanie. Zrezygnowałem z tego i zacząłem używać wspólnego widoku Model między działaniem a fragmentem, co oznacza, że ​​mogę przekazywać zdarzenia między nimi bez potrzeby wywołań zwrotnych lub bezpośredniego dostępu.
— Alin

1
Jak wspomniano w tym raporcie o błędzie, problem issuetracker.google.com/issues/119800853 nie jest to właściwa metoda. Rozważ zmianę prawidłowej odpowiedzi!
— Nicolas Pietri

2
Jak już wspomniano, nie jest to rozwiązanie, jest to obejście do momentu znalezienia właściwego rozwiązania.
— Andrei Toader

1
To działa, wielkie dzięki. Próbowałem getPrimaryNavigationFragment (), jak sugerowano w narzędziu do śledzenia błędów, ale to nie działa.
— Jerry Hu

1
Na podstawie tego rozwiązania stworzyłem funkcję kotlin, która może sprawdzić, czy bieżący fragment jest określonego typu (zamiast id): navHostFragment!!.childFragmentManager.fragments.any { it.javaClass == fragmentClass && it.isVisible }Działa dość podobnie. Mam nadzieję, że pomoże to każdemu!
— P Kuijpers

51

Nie ma sposobu, aby znaleźć bieżącą instancję fragmentu. Możesz jednak uzyskać identyfikator ostatnio dodanego fragmentu, korzystając z poniższego kodu.

navController.currentDestination?.getId()

Zwraca identyfikator w pliku nawigacyjnym. Mam nadzieję że to pomoże.


Dziękuję za Twoją odpowiedź. Musiałem odzyskać fragment, aby móc z nim wejść w interakcję. Ponieważ navController nie może go zwrócić, zakończyłem używanie udostępnionego modelu widoku do rozmowy między działaniem a jego fragmentami
— Alin

8
i oczekuje się, że wrócił id odpowiada ID w xml nav, ale tak nie jest, więc innymi słowy nie można sprawdzić navController.getCurrentDestination () getId () == R.id.myfrag.
— Leres Aldtai

4
@LeresAldtai Pasuje do identyfikatora fragmentu w pliku nawigacyjnym.
— slhddn

1
Dziękuję @slhddn. Twój komentarz pomógł mi i był w stanie odzyskać identyfikator z fragmentu i sprawić, że to zadziała. Dodano tutaj odpowiedź z moim kodem na wypadek, gdyby była przydatna dla kogoś innego. Twoje zdrowie.
— Francislainy Campos

36

Powinieneś spojrzeć na właściwość childFragmentManager primaryNavigationFragment fragmentu nawigacji, gdzie znajduje się odniesienie do aktualnie wyświetlanego fragmentu.

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

3
Ta metoda jest teraz udokumentowana w pliku
— Nicolas Pietri

wydaje się, że jest to problem. obsługa Nawigacja w górę muszę ustawić pasek akcji, aby uzyskać połączenie z onSupportNaigateUp ()
— filthy_wizard

20

To wydaje się najczystszym rozwiązaniem. Aktualizacja: zmieniono funkcję rozszerzenia na właściwość. Dzięki Igor Wojda .

1. Utwórz następujące rozszerzenie

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

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

2. Activityz NavHostFragmentużytku to tak:

val currentFragment = supportFragmentManager.currentNavigationFragment

2
Ładny - taki, który bym zmienił - zdefiniowałbym rozszerzenie jako właściwość FragmentManager.currentNavigationFragment(nie jako funkcję)
— Igor Wojda

17

Użyj addOnDestinationChangedListenerw czynnościach mającychNavHostFragment

W przypadku języka 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());
            }
        });

Dla Kotlina

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

    }

9

to może być późno, ale dla każdego, kto wciąż szuka

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

wtedy możesz zrobić coś takiego

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

zauważ, że to jest dla kotlin, kod java powinien być prawie taki sam



5
navController().currentDestination?.id

poda ci Fragmentidentyfikator twojego obecnego gdzie

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

ten identyfikator to identyfikator, który podałeś w swoim Navigation Graph XMLpliku pod fragmenttagiem. Możesz też porównać, currentDestination.labeljeśli chcesz.


4

Znalazłem działające rozwiązanie.

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

4

Moim wymaganiem jest pobranie aktualnie widocznego fragmentu z Aktywności rodzica, moim rozwiązaniem jest

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

Może to pomóc komuś z tym samym problemem.


masz Kotlin lang?
— IgorGanapolsky

1
To działa idealnie dla mnie, nie wiesz, co jest z innymi odpowiedziami? Może wcześniej to nie działało, ale teraz działa jak urok.
— Wess

3

Z działania, które używa NavHostFragment, możesz użyć poniższego kodu, aby pobrać wystąpienie Active Fragment.

val fragmentInstance = myNavHostFragment?.childFragmentManager?.primaryNavigationFragment

2

Zgodnie z odpowiedzią i komentarzem @slhddn, używam go w następujący sposób i pobieram identyfikator fragmentu z pliku 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

czy możesz sprawdzić, getChildFragmentManager()dzwoniąc z

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

1

Robić :

• w jakimś przycisku w Twoim fragmencie:

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

• W Twojej Aktywności 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()
    } 

1

Jeśli potrzebujesz instancji swojego fragmentu, możesz skorzystać z:

(Aktywność) => supportFragmentManager.fragments.first().childFragmentManager.fragments.first()

Jest bardzo brzydki, ale stamtąd możesz sprawdzić, czy jest to instancja fragmentu, z którym chcesz się pobawić


1

Na Androidx próbowałem wielu metod wyszukiwania wymaganego fragmentu z NavHostFragment. Wreszcie mogłem to złamać poniższą metodą

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
Witamy w SO! Przed wysłaniem odpowiedzi sprawdź ją ... Kod nie jest jasny. Kolejna kwestia: jeśli przynosisz tylko kod, wyjaśnij trochę swoje rozwiązanie, maksymalnie, gdy jest jeszcze 16 odpowiedzi, więc powinieneś wyjaśnić zalety swojej odpowiedzi.
— David García Bodego

1

Pierwszy fragment we fragmencie navhost jest fragmentem bieżącym

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

Czy mógłbyś bardziej rozwinąć swoją odpowiedź? Może wyjaśniając swój kod.
— Dwadzieścia

Odpowiedzi zawierające tylko kod są odradzane. Proszę wyjaśnić, dlaczego i jak Twoja odpowiedź rozwiązuje problem.
— Igor F.

1

Najpierw otrzymujesz aktualny fragment przez id, potem możesz znaleźć fragment, w zależności od tego id.

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

0

I połączeniu Andrei Toaders odpowiedź i Mukul Bhardwajs odpowiedź ze związkiemOnBackStackChangedListener .

Jako atrybut:

private FooFragment fragment;

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

W ten sposób mogę uzyskać fragment, który zostanie wyświetlony później. Można nawet zapętlić fragsi sprawdzić wiele fragmentów za pomocą instanceof.


0

Muszę to zrobić, aby ustawić dolny widok nawigacji i zrobiłem to w ten sposób:

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

0

Najlepszym sposobem na osiągnięcie tego jest zarejestrowanie wywołania zwrotnego cyklu życia fragmentu .

Zgodnie z pierwotnym pytaniem, jeśli twój NavHostFragmentjest zdefiniowany jako ...

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

Następnie w swojej głównej działalności 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
        }
    }
)

Inne metody wywołania zwrotnego mogą zostać zastąpione, aby pasowały do ​​twoich wymagań.


0

To rozwiązanie zadziała w większości sytuacji Spróbuj tego:

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

albo to:

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

0

Ten kod działa ode mnie

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

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

0

To działa dla mnie

(navController.currentDestination as FragmentNavigator.Destination).className 

Zwróci canonicalNametwój fragment


1
To wygląda jak Reflection
— IgorGanapolsky

0

to może być późno, ale może pomóc komuś później.

jeśli używasz zagnieżdżonych nawigacji, możesz uzyskać taki identyfikator w Kotlinie

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

a to jest jeden z fragmentów w fragment_nav_host.xml

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

i w ten sposób możesz sprawdzić wszystko, czego potrzebujesz

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

0

Wreszcie działające rozwiązanie dla tych z Was, którzy wciąż szukają. W rzeczywistości jest to bardzo proste i można to osiągnąć za pomocą oddzwaniania.

wykonaj następujące kroki:

  1. Utwórz detektor wewnątrz fragmentu, dla którego chcesz pobrać instancję z działania 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. Zaimplementuj odbiornik we fragmencie / działaniu hosta

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

I to wszystko, czego potrzebujesz do wykonania tej pracy. Mam nadzieję, że komuś to pomoże


0

Najlepszy sposób, w jaki mogłem się dowiedzieć z 1 fragmentem, Kotlin, użycie nawigacji:

W pliku układu dodaj tag: "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"
        />

W Twojej aktywności:

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

Uwaga: findFragmentfunkcja rozszerzenia znajduje się w androidx.fragment.apppakiecie i jest to funkcja ogólna


-2

W swojej działalności zdefiniuj obiekt fragmentu. Z każdego fragmentu wywołaj tę metodę, przekazując obiekt fragmentu, na przykład obiekt statycznego fragmentu zostanie za każdym razem nadpisany przez bieżący fragment pokazujący.

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

Możesz również ustawić wywołanie zwrotne z fragmentu do działania, aby uzyskać lepsze podejście i przekazać bieżący obiekt fragmentu.


2
Dziękuję za Twój czas. Szukałem czegoś do użycia z samego komponentu nawigacji.
— Alin
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.