Android Navigation Architecture Component - Aktuelles sichtbares Fragment abrufen


95

Bevor ich die Navigationskomponente ausprobierte, führte ich Fragmenttransaktionen manuell durch und verwendete das Fragment-Tag, um das aktuelle Fragment abzurufen.

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

Jetzt habe ich in meinem Hauptaktivitätslayout so etwas wie:

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

Wie kann ich das aktuell angezeigte Fragment von der Navigationskomponente abrufen? Tun

supportFragmentManager.findFragmentById(R.id.nav_host)

gibt a zurück NavHostFragmentund ich möchte mein angezeigtes 'MyFragment` abrufen.

Vielen Dank.


4
Es gibt keine Möglichkeit, das aktuelle Fragment stackoverflow.com/a/50689587/1268507 abzurufen. Was möchten Sie erreichen? Vielleicht gibt es eine andere Lösung dafür.
Alex

Ich brauchte von meinem Fragment, um eine Kameraabsicht zu starten, ein Bild aufzunehmen und das aufgenommene Bild zu verwenden. Zu diesem Zweck habe ich eine Aktivität gestartet und auf das Ergebnis onActivityResultmeiner Hauptaktivität gewartet . Da ich das Fragment nicht bekommen konnte, habe ich das alles einfach in das Fragment selbst verschoben und scheint zu funktionieren.
Alin

Möchten Sie eine Operation für dieses Fragmentobjekt ausführen? Oder möchten Sie nur, welches Fragment angezeigt wird?
Ranjan

Antworten:


101

Ich habe es geschafft, einen Weg für jetzt zu finden und es ist wie folgt:

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

Falls Sie natürlich wissen, dass es das erste Fragment ist. Ich untersuche immer noch einen Weg ohne diesen. Ich bin damit einverstanden, dass es nicht der beste Weg ist, aber das sollte vorerst etwas sein.


6
Es ist großartig, eine funktionierende Lösung zu sehen. Ich habe dies aufgegeben und angefangen, ein gemeinsames viewModel zwischen der Aktivität und dem Fragment zu verwenden. Dies bedeutet, dass ich Ereignisse zwischen ihnen übergeben kann, ohne dass Rückrufe oder direkter Zugriff erforderlich sind.
Alin

Wie in diesem Fehlerbericht issuetracker.google.com/issues/119800853 erwähnt, ist dies nicht die richtige Methode, um dies zu tun. Bitte ändern Sie die richtige Antwort!
Nicolas Pietri

2
Wie bereits erwähnt, handelt es sich nicht um die Lösung, sondern um eine Problemumgehung, bis eine geeignete Lösung gefunden wurde.
Andrei Toader

1
Das funktioniert, vielen Dank. Ich habe versucht, getPrimaryNavigationFragment () wie im Bug-Tracker vorgeschlagen, aber es erledigt den Job nicht.
Jerry Hu

1
Basierend auf dieser Lösung habe ich eine Kotlin-Funktion erstellt, mit der überprüft werden kann, ob das aktuelle Fragment von einem bestimmten Typ ist (anstelle der ID): navHostFragment!!.childFragmentManager.fragments.any { it.javaClass == fragmentClass && it.isVisible }Funktioniert ziemlich ähnlich. Hoffe es wird jedem helfen!
P Kuijpers

49

Ich kann die aktuelle Fragmentinstanz nicht abrufen. Sie können jedoch die ID des zuletzt hinzugefügten Fragments mithilfe des folgenden Codes abrufen.

navController.currentDestination?.getId()

Es gibt die ID in der Navigationsdatei zurück. Hoffe das hilft.


Vielen Dank für Ihre Antwort. Ich musste das Fragment abrufen, damit ich damit interagieren konnte. Da navController es nicht zurückgeben kann, habe ich die Verwendung eines gemeinsam genutzten Ansichtsmodells beendet, um zwischen Aktivität und ihren Fragmenten zu sprechen
Alin

7
Ich habe erwartet, dass die zurückgegebene ID mit der ID in der Navi-
XML übereinstimmt

4
@LeresAldtai Entspricht der ID des Fragments in der Navigationsdatei.
Slhddn

1
Vielen Dank, dass Sie @slhddn. Ihr Kommentar hat mir geholfen und konnte die ID aus dem Fragment abrufen und diese Funktion ausführen. Hier wurde eine Antwort mit meinem Code hinzugefügt, falls dies für jemand anderen nützlich ist. Prost.
Francislainy Campos

32

Sie sollten sich die Eigenschaft childFragmentManager primaryNavigationFragment Ihres Navigationsfragments ansehen. Hier wird auf das aktuell angezeigte Fragment verwiesen.

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

2
Diese Methode ist jetzt in fragmentManager issuetracker.google.com/issues/119800853
Nicolas Pietri

Dies scheint ein Problem zu sein. Behandlung der Navigation Ich muss die Aktionsleiste einrichten, um einen Aufruf von onSupportNaigateUp ()
filthy_wizard

18

Dies scheint die sauberste Lösung zu sein. Update: Erweiterungsfunktion in Eigenschaft geändert. Danke Igor Wojda .

1. Erstellen Sie die folgende Erweiterung

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

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

2. Activitymit NavHostFragmentihm wie folgt Gebrauch:

val currentFragment = supportFragmentManager.currentNavigationFragment

2
Schön - eine, die ich ändern würde - ich würde Erweiterung als Eigenschaft definieren FragmentManager.currentNavigationFragment(nicht als Funktion)
Igor Wojda

16

Verwendung addOnDestinationChangedListenerbei Aktivitäten mitNavHostFragment

Für 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());
            }
        });

Für Kotlin

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

    }

8

Dies könnte spät sein, aber für alle, die noch suchen

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

dann kannst du so etwas machen

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

Beachten Sie, dass dies für Kotlin gilt. Der Java-Code sollte fast identisch sein



5
navController().currentDestination?.id

geben Sie Ihre aktuelle FragmentID wo

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

Diese ID ist die ID, die Sie in Ihrer Navigation Graph XMLDatei unter dem fragmentTag angegeben haben. Sie können auch vergleichen, currentDestination.labelwenn Sie möchten.


4

Arbeitslösung gefunden.

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

In einer Aktivität, die verwendet wird NavHostFragment, können Sie den folgenden Code verwenden, um die Instanz von abzurufen Active Fragment.

val fragmentInstance = myNavHostFragment?.childFragmentManager?.primaryNavigationFragment

3

Meine Anforderung ist es, das aktuell sichtbare Fragment aus der übergeordneten Aktivität zu erhalten. Meine Lösung lautet

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

Dies kann jemandem mit dem gleichen Problem helfen.


hast du Kotlin lang?
IgorGanapolsky

1
Das funktioniert perfekt für mich, nicht sicher, was mit den anderen Antworten los ist? Vielleicht hat das vorher nicht funktioniert, aber jetzt funktioniert es wie ein Zauber.
Wess

2

Gemäß @slhddn Antwort und Kommentar verwende ich es so und rufe die Fragment- ID aus der Datei nav_graph.xml ab :

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

können Sie bitte mit getChildFragmentManager()Anruf mit überprüfen

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

1

Machen :

• in einer Schaltfläche in Ihrem Fragment:

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

• In Ihrer Aktivität 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

Wenn Sie die Instanz Ihres Fragments benötigen, können Sie Folgendes ausführen:

(Aktivität) => supportFragmentManager.fragments.first().childFragmentManager.fragments.first()

Es ist sehr hässlich, aber von dort aus können Sie überprüfen, ob es sich um eine Instanz des Fragments handelt, mit dem Sie herumspielen möchten


1

Unter Androidx habe ich viele Methoden ausprobiert, um das erforderliche Fragment aus herauszufinden NavHostFragment. Endlich konnte ich es mit der folgenden Methode knacken

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
Willkommen bei SO! Bitte überprüfen Sie diese, bevor Sie Ihre Antwort veröffentlichen ... Der Code ist nicht klar. Ein weiteres Problem: Wenn Sie nur Code mitbringen, erläutern Sie Ihre Lösung ein wenig, maximal bei 16 weiteren Antworten. Erklären Sie daher die Vorteile Ihrer Antwort.
David García Bodego

1

Das erste Fragment im Navhost-Fragment ist das aktuelle Fragment

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

Könnten Sie Ihre Antwort etwas näher erläutern? Vielleicht durch Erklären Ihres Codes.
Zwanzig

Von Nur-Code-Antworten wird abgeraten. Bitte erläutern Sie, warum und wie Ihre Antwort das Problem löst.
Igor F.

1

Zuerst erhalten Sie das aktuelle Fragment anhand der ID, dann können Sie das Fragment abhängig von dieser ID finden.

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

0

Ich kombinierte die Antwort von Andrei Toaders und die Antwort von Mukul Bhardwajs mit einerOnBackStackChangedListener .

Als Attribut:

private FooFragment fragment;

In meinem 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;
        }
});

Auf diese Weise kann ich ein Fragment erhalten, das später angezeigt wird. Man kann sogar in der Lage sein, eine Schleife zu erstellen fragsund mehrere Fragmente mit zu überprüfen instanceof.


0

Ich muss dies tun, um eine untere Navigationsansicht einzurichten, und ich habe es folgendermaßen gemacht:

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

0

Der beste Weg, dies zu erreichen, besteht darin, einen Rückruf des Fragmentlebenszyklus zu registrieren .

Gemäß der ursprünglichen Frage, wenn Ihre NavHostFragmentdefiniert ist als ...

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

Dann in Ihrer Haupttätigkeit 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
        }
    }
)

Andere Rückrufmethoden werden möglicherweise überschrieben, um Ihren Anforderungen zu entsprechen.


0

Diese Lösung funktioniert in den meisten Situationen. Versuchen Sie Folgendes:

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

oder dieses:

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

0

Dieser Code funktioniert von mir

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

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

0

Das funktioniert bei mir

(navController.currentDestination as FragmentNavigator.Destination).className 

Es wird canonicalNamevon Ihrem Fragment zurückkehren


Das sieht aus wie Reflection
IgorGanapolsky

0

Dies kann zu spät sein, kann aber später jemandem helfen.

Wenn Sie verschachtelte Navigationen verwenden, können Sie in Kotlin eine solche ID erhalten

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

und dies ist eines der Fragmente in fragment_nav_host.xml

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

und Sie können alles, was Sie brauchen, so überprüfen

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

0

Endlich die funktionierende Lösung für diejenigen unter Ihnen, die noch suchen. Eigentlich ist es sehr einfach und Sie können dies durch Rückruf erreichen.

folge diesen Schritten:

  1. Erstellen Sie einen Listener innerhalb des Fragments, für das Sie die Instanz aus der Aktivität onCreate () abrufen möchten.

    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. Implementieren Sie Ihren Listener im Host Fragment / Activity

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

Und das ist alles, was Sie brauchen, um die Arbeit zu erledigen. Hoffen Sie, dass dies jemandem hilft


0

Der beste Weg, den ich mit 1 Fragment, Kotlin, Navigationsnutzung herausfinden konnte:

Fügen Sie in Ihrer Layoutdatei das Tag hinzu: "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"
        />

Zu Ihrer Aktivität:

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

Hinweis: Die findFragmentErweiterungsfunktion befindet sich im androidx.fragment.appPaket und ist eine generische Funktion


-2

Definieren Sie in Ihrer Aktivität ein Fragmentobjekt. Rufen Sie diese Methode von jedem Fragment aus auf, indem Sie ein Fragmentobjekt übergeben, z. B. Das statische Fragmentobjekt wird jedes Mal durch das aktuell angezeigte Fragment überschrieben.

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

Sie können für einen besseren Ansatz auch den Rückruf von Fragment zu Aktivität festlegen und Ihr aktuelles Fragmentobjekt übergeben.


2
Vielen Dank für Ihre Zeit. Ich suchte nach etwas, das ich von der Navigationskomponente selbst verwenden konnte.
Alin
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.