Komponen Arsitektur Navigasi Android - Dapatkan fragmen yang terlihat saat ini

95

Sebelum mencoba komponen Navigasi, saya biasa melakukan transaksi fragmen secara manual dan menggunakan tag fragmen untuk mengambil fragmen saat ini.

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

Sekarang di layout aktivitas utama saya, saya memiliki sesuatu seperti:

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

Bagaimana cara mendapatkan kembali fragmen yang ditampilkan saat ini oleh komponen Navigasi? Perbuatan

supportFragmentManager.findFragmentById(R.id.nav_host)

mengembalikan a NavHostFragmentdan saya ingin mengambil 'MyFragment` yang ditampilkan.

Terima kasih.

Alin
sumber
4
Tidak ada cara untuk mengambil kembali fragmen stackoverflow.com/a/50689587/1268507 Apa yang ingin Anda capai? mungkin ada solusi lain untuk itu.
Alex
Saya perlu dari fragmen saya untuk memulai tujuan kamera, mengambil gambar dan menggunakan gambar yang diambil. Untuk itu, saya sudah memulai sebuah aktivitas dan menunggu hasil di onActivityResultaktivitas utama saya. Karena saya tidak bisa mendapatkan fragmen, saya cukup memindahkan semua ini ke dalam fragmen itu sendiri dan sepertinya berhasil.
Alin
Apakah Anda ingin melakukan operasi pada objek fragmen tersebut. Atau Anda ingin fragmen mana yang ditampilkan?
Ranjan

Jawaban:

101

Saya berhasil menemukan cara untuk saat ini dan itu adalah sebagai berikut:

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

Dalam kasus tentu saja Anda tahu itu adalah fragmen pertama. Saya masih menyelidiki cara tanpa ini. Saya setuju ini bukan cara terbaik tetapi itu harus menjadi sesuatu untuk saat ini.

Andrei Toader
sumber
6
Sangat menyenangkan melihat solusi yang berhasil. Saya menyerah pada hal ini dan mulai menggunakan viewModel bersama antara aktivitas dan fragmen. Ini berarti saya bisa meneruskan kejadian di antara mereka tanpa memerlukan callback atau akses langsung.
Alin
Seperti disebutkan dalam masalah laporan bug ini Issuetracker.google.com/issues/119800853 ini bukan metode yang benar untuk melakukannya. Mohon pertimbangkan untuk mengubah jawaban yang benar!
Nicolas Pietri
2
Seperti yang disebutkan, ini bukanlah solusi, ini adalah solusi sampai solusi yang tepat ditemukan.
Andrei Toader
1
Ini berhasil, terima kasih banyak. Saya mencoba getPrimaryNavigationFragment () seperti yang disarankan dalam pelacak bug tetapi tidak menyelesaikan pekerjaan.
Jerry Hu
1
Berdasarkan solusi ini, saya telah membuat fungsi kotlin yang dapat memeriksa apakah fragmen saat ini berjenis tertentu (bukan id): navHostFragment!!.childFragmentManager.fragments.any { it.javaClass == fragmentClass && it.isVisible }Bekerja sangat mirip. Semoga bisa membantu siapa pun!
P Kuijpers
49

Tidak ada cara yang dapat saya temukan untuk mengambil instance fragmen saat ini. Namun, Anda bisa mendapatkan ID dari fragmen yang terakhir ditambahkan menggunakan kode di bawah ini.

navController.currentDestination?.getId()

Ini mengembalikan ID dalam file navigasi. Semoga ini membantu.

slhddn
sumber
Terima kasih atas jawaban Anda. Saya perlu mengambil fragmen agar saya dapat berinteraksi dengannya. Karena navController tidak dapat mengembalikannya, saya mengakhiri penggunaan model tampilan bersama untuk membicarakan antara aktivitas dan fragmennya
Alin
7
saya berharap bahwa id yang dikembalikan cocok dengan id di nav xml, tetapi tidak, jadi dengan kata lain Anda tidak dapat memeriksa navController.getCurrentDestination (). getId () == R.id.myfrag
Leres Aldtai
4
@LeresAldtai Ini cocok dengan ID fragmen di file navigasi.
slhddn
1
Terima kasih @slhddn. Komentar Anda membantu saya dan dapat mengambil id dari fragmen dan membuat ini berfungsi. Menambahkan jawaban di sini dengan kode saya jika berguna untuk orang lain. Bersulang.
Francislainy Campos
32

Anda harus melihat properti childFragmentManager primaryNavigationFragment dari fragmen nav Anda, di sinilah fragmen yang ditampilkan saat ini direferensikan.

val navHost = supportFragmentManager.findFragmentById(R.id.main_nav_fragment)
    navHost?.let { navFragment ->
        navFragment.childFragmentManager.primaryNavigationFragment?.let {fragment->
                //DO YOUR STUFF
        }
    }
}
Nicolas Pietri
sumber
2
Metode ini sekarang didokumentasikan di fragmentManager Issuetracker.google.com/issues/119800853
Nicolas Pietri
ini sepertinya sedikit masalah. menangani navigasi Up, saya harus menyiapkan bilah tindakan untuk mendapatkan panggilan ke onSupportNaigateUp ()
filthy_wizard
18

Ini sepertinya solusi terbersih. Pembaruan: Fungsi ekstensi diubah menjadi properti. Terima kasih Igor Wojda .

1. Buat ekstensi berikut

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

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

2. Dalam Activitydengan NavHostFragmentpenggunaan seperti ini:

val currentFragment = supportFragmentManager.currentNavigationFragment
Aleksandar Ilic
sumber
2
Bagus - yang ini saya akan ubah - Saya akan mendefinisikan ekstensi sebagai properti FragmentManager.currentNavigationFragment(bukan sebagai fungsi)
Igor Wojda
16

Gunakan addOnDestinationChangedListenerdalam aktivitas memilikiNavHostFragment

Untuk 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());
            }
        });

Untuk Kotlin

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

    }
Mukul Bhardwaj
sumber
8

ini mungkin terlambat tetapi bagi siapa saja yang masih melihat

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

maka Anda dapat melakukan sesuatu seperti

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

perhatikan bahwa ini untuk kotlin, kode java harus hampir sama

MoTahir
sumber
2
Ini adalah kode java, stackoverflow.com/a/60539704/4291272
Faisal Shaikh
5
navController().currentDestination?.id

akan memberi Anda Fragmentid Anda saat ini di mana

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

id ini adalah id yang telah Anda berikan di Navigation Graph XMLfile Anda di bawah fragmenttag. Anda juga bisa membandingkan currentDestination.labeljika Anda mau.

Abhishek Bansal
sumber
4

Menemukan solusi yang berfungsi.

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
sumber
3

Dari Aktivitas yang menggunakan NavHostFragment, Anda dapat menggunakan kode di bawah ini untuk mengambil instance dari Active Fragment.

val fragmentInstance = myNavHostFragment?.childFragmentManager?.primaryNavigationFragment
jzarsuelo.dll
sumber
3

Persyaratan saya adalah mendapatkan fragmen yang terlihat saat ini dari aktivitas Induk, solusi saya adalah

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

Ini dapat membantu seseorang dengan masalah yang sama.

CLIFFORD PY
sumber
punya Kotlin lang?
IgorGanapolsky
1
Ini bekerja dengan sempurna untuk saya, tidak yakin ada apa dengan jawaban lain? Mungkin ini tidak berfungsi sebelumnya tetapi berfungsi seperti pesona sekarang.
Wess
2

Sesuai jawaban dan komentar @slhddn, inilah cara saya menggunakannya dan mengambil id fragmen dari file 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
sumber
1

dapatkah Anda getChildFragmentManager()menghubungi dengan

NavHostFragment fragment = supportFragmentManager.findFragmentById(R.id.nav_host);
MyFragment frag = (MyFragment) fragment.getChildFragmentManager().findFragmentByTag(tag);
Shiam Shabbir Himel
sumber
1

Buat:

• di beberapa tombol di fragmen Anda:

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

• Dalam Aktivitas Anda 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
sumber
1

Jika Anda membutuhkan instance dari fragmen Anda, Anda dapat menggunakan:

(Aktivitas) => supportFragmentManager.fragments.first().childFragmentManager.fragments.first()

Ini sangat jelek tetapi dari sana Anda dapat memeriksa apakah itu adalah instance dari fragmen yang ingin Anda mainkan

Ailelame
sumber
1

Di Androidx, saya telah mencoba banyak metode untuk menemukan fragmen yang dibutuhkan NavHostFragment. Akhirnya saya bisa memecahkannya dengan metode di bawah ini

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
sumber
1
Selamat datang di SO! Tolong, sebelum Anda memposting jawaban Anda, periksa ... Kode tidak jelas. Masalah lain: jika Anda hanya membawa kode, jelaskan solusi Anda sedikit, maksimal jika ada 16 jawaban lagi, jadi Anda harus menjelaskan Pro dari jawaban Anda.
David García Bodego
1

Fragmen pertama dalam fragmen navhost adalah fragmen saat ini

Fragment navHostFragment = getSupportFragmentManager().getPrimaryNavigationFragment();
if (navHostFragment != null) 
{Fragment fragment = navHostFragment.getChildFragmentManager().getFragments().get(0);}
Eren Tüfekçi
sumber
Bisakah Anda menjelaskan jawaban Anda lebih lanjut? Mungkin dengan menjelaskan kode Anda.
Dua puluh
Jawaban hanya kode tidak disarankan. Harap berikan penjelasan mengapa dan bagaimana jawaban Anda menyelesaikan masalah.
Igor F.
1

Pertama, Anda mendapatkan fragmen saat ini berdasarkan id, lalu Anda dapat menemukan fragmen tersebut, bergantung pada id itu.

int id = navController.getCurrentDestination().getId();
Fragment fragment = getSupportFragmentManager().findFragmentById(id);
Abu-Bakr
sumber
0

Saya menggabungkan jawaban Andrei Toaders dan jawaban Mukul Bhardwajs dengan OnBackStackChangedListener.

Sebagai atribut:

private FooFragment fragment;

Dalam saya 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;
        }
});

Dengan cara ini saya bisa mendapatkan sebuah fragmen, yang akan ditampilkan nanti. Seseorang bahkan mungkin dapat melakukan loop fragsdan memeriksa beberapa fragmen dengan instanceof.

Xerusial
sumber
0

Saya perlu melakukan ini untuk menyiapkan tampilan navigasi bawah dan saya melakukannya dengan cara ini:

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

Cara terbaik untuk melakukannya adalah dengan mendaftarkan callback siklus proses fragmen .

Sesuai pertanyaan awal, jika Anda NavHostFragmentdidefinisikan sebagai ...

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

Kemudian dalam aktivitas utama Anda 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
        }
    }
)

Metode panggilan balik lainnya mungkin diganti agar sesuai dengan kebutuhan Anda.

miguelt
sumber
0

Solusi ini akan berfungsi di banyak situasi. Coba ini:

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

atau ini:

val mainFragment = fragmentManager?.primaryNavigationFragment?.parentFragment as MainFragment
pengguna2288580
sumber
0

Kode ini bekerja dari saya

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

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

Ini berhasil untuk saya

(navController.currentDestination as FragmentNavigator.Destination).className 

Ini akan mengembalikan canonicalNamefragmen Anda

Inc
sumber
Ini terlihat seperti Refleksi
IgorGanapolsky
0

ini mungkin terlambat tetapi dapat membantu seseorang nanti.

jika Anda menggunakan navigasi bersarang, Anda bisa mendapatkan id seperti ini di Kotlin

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

dan ini adalah salah satu fragmen di fragment_nav_host.xml

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

dan Anda dapat memeriksa semua yang Anda butuhkan seperti ini

if (nav.id == R.id.sampleFragment || nav.label == "SampleFragment"){}
Payam Mf
sumber
0

Akhirnya solusi kerja untuk Anda yang masih mencari. Sebenarnya sangat sederhana dan Anda dapat melakukannya dengan menggunakan callback.

ikuti langkah ini:

  1. Buat listener di dalam fragmen yang Anda inginkan untuk mendapatkan instance dari aktivitas 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. Implementasikan listener Anda di Fragment / Activity host

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

Dan itu semua yang Anda butuhkan untuk melakukan pekerjaan itu. Berharap ini membantu seseorang

Ecclesiaste Panda
sumber
0

Cara terbaik yang bisa saya ketahui dengan 1 fragmen, Kotlin, penggunaan Navigasi:

Pada file tata letak Anda, tambahkan 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"
        />

Pada Aktivitas Anda:

val navHostFragment = supportFragmentManager.findFragmentByTag("main_fragment_tag") as NavHostFragment
val mainFragment = navHostFragment.childFragmentManager.fragments[0] as MainFragment
mainFragment.mainFragmentMethod()
patel dhruval
sumber
0
val fragment: YourFragment? = yourNavHost.findFragment()

Catatan: findFragmentfungsi ekstensi terletak di dalam androidx.fragment.apppaket, dan itu adalah fungsi generik

Narek Hayrapetyan
sumber
-2

Dalam aktivitas Anda, tentukan objek fragmen. Dan dari setiap fragmen, panggil metode ini dengan meneruskan objek fragmen, seperti Jadi objek fragmen statis akan diganti dengan fragmen yang ditampilkan saat ini setiap saat.

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

Anda juga bisa menyetel callback dari fragmen ke aktivitas, untuk pendekatan yang lebih baik dan meneruskan objek fragmen Anda saat ini.

Ranjan
sumber
2
Terima kasih atas waktu Anda. Saya sedang mencari sesuatu untuk digunakan dari komponen Navigasi itu sendiri.
Alin