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.

Alin
źródło
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ś.

Andrei Toader
źródło
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.

slhddn
źródło
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
        }
    }
}
Nicolas Pietri
źródło
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
Aleksandar Ilic
źródło
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);

    }
Mukul Bhardwaj
źródło
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

MoTahir
źródło
2
Oto kod java, stackoverflow.com/a/60539704/4291272
Faisal Shaikh
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.

Abhishek Bansal
źródło
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
    }
}
Tomas Kuhn
źródło
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.

CLIFFORD PY
źródło
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
jzarsuelo
źródło
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)
        }

    }

}

}
Francislainy Campos
źródło
1

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

NavHostFragment fragment = supportFragmentManager.findFragmentById(R.id.nav_host);
MyFragment frag = (MyFragment) fragment.getChildFragmentManager().findFragmentByTag(tag);
Shiam Shabbir Himel
źródło
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()
    } 
Paulo Linhares - Packapps
źródło
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ć

Ailelame
źródło
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;
}
Karthik
źródło
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);}
Eren Tüfekçi
źródło
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);
Abu bakr
źródło
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.

Xerusial
źródło
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)
Roque Rueda
źródło
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ń.

miguelt
źródło
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
user2288580
źródło
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;
}
Faisal Shaikh
źródło
0

To działa dla mnie

(navController.currentDestination as FragmentNavigator.Destination).className 

Zwróci canonicalNametwój fragment

Inc
źródło
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"){}
Payam Mf
źródło
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

Ecclesiaste Panda
źródło
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()
patel dhruval
źródło
0
val fragment: YourFragment? = yourNavHost.findFragment()

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

Narek Hayrapetyan
źródło
-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.

Ranjan
źródło
2
Dziękuję za Twój czas. Szukałem czegoś do użycia z samego komponentu nawigacji.
Alin