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.