Fragmen dalam Fragmen

145

Saya ingin tahu apakah ini sebenarnya bug di API Android:

Saya memiliki pengaturan seperti ini:

┌----┬---------┐
|    |         |
|  1 |    2    |
|    |┌-------┐|
|    ||       ||
|    ||   3   ||
└----┴┴-------┴┘
  1. Adalah menu yang memuat fragmen # 2 (Layar pencarian) di panel kanan.
  2. Adalah layar pencarian yang berisi fragmen # 3, yang merupakan daftar hasil.
  3. Daftar hasil digunakan di beberapa tempat (termasuk sebagai fragmen tingkat tinggi yang berfungsi dengan haknya sendiri).

Fungsionalitas ini berfungsi dengan baik pada telepon (Di mana 1 & 2 dan 3 adalah ActivityFragments).

Namun, ketika saya menggunakan kode ini:

    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();       
    Fragment frag = new FragmentNumber2();
    if(toLoad != null) frag.setArguments(toLoad);
    transaction.replace(R.id.rightPane, frag);      
    transaction.commit();

Di mana R.id.leftPanedan R.id.rightPaneberada <fragment>dalam tata letak linier horizontal.

Ini adalah pemahaman saya bahwa kode di atas menghapus fragmen yang penduduk dan kemudian menggantinya dengan fragmen baru. Brilliant ... Jelas bukan itu yang terjadi karena ketika kode ini berjalan kedua kalinya Anda mendapatkan pengecualian berikut:

07-27 15:22:55.940: ERROR/AndroidRuntime(8105): Caused by: java.lang.IllegalArgumentException: Binary XML file line #57: Duplicate id 0x7f080024, tag null, or parent id 0x0 with another fragment for FragmentNumber3

Ini disebabkan karena wadah untuk FragmentNumber3 telah digandakan dan tidak lagi memiliki ID unik. Fragmen awal belum dihancurkan (?) Sebelum yang baru ditambahkan (dalam pikiran saya itu berarti belum diganti ).

Dapatkah seseorang memberi tahu saya jika ini mungkin ( jawaban ini menunjukkan tidak) atau ini bug?

Graeme
sumber
1
kemungkinan rangkap dari Fragment Inside Fragment
rds
6
@ rds ini adalah pertanyaan kuno, sedikit gunanya untuk ditandai sebagai duplikat.
pietv8x

Jawaban:

203

Fragmen bersarang saat ini tidak didukung. Mencoba memasukkan fragmen ke dalam UI dari fragmen lain akan menghasilkan perilaku yang tidak terdefinisi dan kemungkinan rusak.

Pembaruan : Nested fragmen didukung pada Android 4.2 (dan Android Support Library rev 11): http://developer.android.com/about/versions/android-4.2.html#NestedFragments

CATATAN (sesuai dokumen ini ): " Catatan: Anda tidak dapat mengembang tata letak menjadi fragmen ketika tata letak itu mencakup a <fragment>. Fragmen bersarang hanya didukung ketika ditambahkan ke fragmen secara dinamis. "

hackbod
sumber
14
Tidak didukung karena itu bukan tujuan desain untuk implementasi awal. Saya telah mendengar banyak permintaan untuk fitur ini, jadi mungkin akan dilakukan di beberapa titik, tetapi seperti biasa ada banyak hal lain yang bersaing dengannya dalam prioritas.
hackbod
4
Saya mengelola ini dengan memperluas FragmentActivity, FragmentManager, dan FragmentTransaction. Dasar pemikirannya adalah memperpanjang DeferringFragmentActivity dalam aktivitas saya, memberikan api yang sama sehingga tidak ada perubahan kode lainnya. Ketika saya memanggil getFragmentManager, saya mendapatkan instance DeferringFragmentManager, dan ketika saya menelepon beginTransaction, saya mendapatkan DeferredTransaction. Transaksi ini menyimpan POJO dengan metode dan argumen yang disebut. Ketika komit adalah panggilan, kami mencari segala transaksi yang tertunda terlebih dahulu. Setelah semua transaksi dilakukan, kami memulai transaksi nyata dan menjalankan semua metode tersimpan dengan args.
dskinner
11
Poin itu sekarang. Sarang Fragmentsekarang menjadi bagian dari API Android, yay! developer.android.com/about/versions/… .
Alex Lockwood
9
Wow, sungguh mimpi buruk: jika Anda menggunakan <fragment> pada Fragmen, dan Fragmen itu terjadi menggunakan fragmen anak, itu tidak gagal dengan kesalahan yang jelas ("tidak dapat menambahkan fragmen anak ke fragmen tata letak") - itu gagal secara misterius dengan pengecualian seperti "fragmen tidak membuat tampilan". Ada beberapa jam waktu debugging ...
Glenn Maynard
6
@ MartínMarconcini yakin tapi itu sama sekali tidak didasarkan pada fungsionalitas yang disediakan oleh API. Jika sesuatu tidak diizinkan harus didokumentasikan dengan jelas, jangan diserahkan kepada pengembang untuk menarik rambut mereka keluar karena ada sesuatu yang tidak bekerja seperti yang Anda harapkan.
dcow
98

Fragmen bersarang didukung di Android 4.2 dan yang lebih baru

Pustaka Dukungan Android juga sekarang mendukung fragmen bersarang , sehingga Anda dapat menerapkan desain fragmen bertingkat pada Android 1.6 dan lebih tinggi.

Untuk membuat fragmen, cukup panggil getChildFragmentManager () pada Fragmen tempat Anda ingin menambahkan fragmen. Ini mengembalikan FragmentManager yang dapat Anda gunakan seperti biasa dari aktivitas tingkat atas untuk membuat transaksi fragmen. Misalnya, inilah beberapa kode yang menambahkan fragmen dari dalam kelas Fragmen yang ada:

Fragment videoFragment = new VideoPlayerFragment();
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
transaction.add(R.id.video_fragment, videoFragment).commit();

Untuk mendapatkan lebih banyak gagasan tentang fragmen bersarang, silakan kunjungi tutorial ini
Bagian 1
Bagian 2
Bagian 3

dan di sini ada posting SO yang membahas tentang praktik terbaik untuk fragmen bersarang .

Raneez Ahmed
sumber
Kerugian utama dari Nestedfragment adalah kita tidak bisa memanggil optionmenu dari childfragment :( jika kita menggunakan ABS!
LOG_TAG
Bisakah Anda melihat masalah saya ?? Sangat mirip .. stackoverflow.com/questions/32240138/… . Bagi saya framnet anak tidak meningkat dari kode
Nicks
33

.. Anda dapat membersihkan fragmen bersarang Anda di metode fragmen induk destroyview:

@Override
    public void onDestroyView() {

      try{
        FragmentTransaction transaction = getSupportFragmentManager()
                .beginTransaction();

        transaction.remove(nestedFragment);

        transaction.commit();
      }catch(Exception e){
      }

        super.onDestroyView();
    }
Furykid
sumber
4
Jika Anda melakukan beberapa pengujian siklus hidup dengan SetAlwaysFinish ( bricolsoftconsulting.com/2011/12/23/… ), Anda akan melihat bahwa kode ini menyebabkan kesalahan ketika aktivitas lain berjalan di atas dengan selalu selesai diaktifkan (IllegalStateException: Tidak dapat melakukan tindakan ini setelah onSaveInstanceState). Membungkus kode di atas dalam try / catch bukanlah solusi yang paling elegan tetapi tampaknya membuat semuanya berfungsi.
Theo
Ini hampir berhasil. Kemudian saya mendapat Stackoverflow menggambar UI. Jelas menghindari fragmen bersarang ...
neteinstein
14

Saya memiliki aplikasi yang sedang saya kembangkan yang ditata mirip dengan Tab di Action Bar yang meluncurkan fragmen, beberapa Fragmen ini memiliki beberapa Fragmen yang tertanam di dalamnya.

Saya mendapatkan kesalahan yang sama ketika saya mencoba menjalankan aplikasi. Sepertinya jika Anda instantiate Fragmen dalam tata letak xml setelah tab tidak dipilih dan kemudian dipilih kembali saya akan mendapatkan kesalahan inflator.

Saya memecahkan masalah ini dengan mengganti semua fragmen dalam xml dengan Linearlayouts dan kemudian menggunakan manajer fragmen / transaksi fragmen untuk instantiate fragmen yang semuanya berfungsi dengan baik setidaknya pada tingkat pengujian sekarang.

Saya harap ini membantu Anda.

draksia
sumber
Adakah yang bisa mengomentari efektivitas pendekatan ini? Saya merasa tidak beruntung bisa menggunakan Fragmen hanya sedalam satu level - mungkin juga tidak menggunakannya sama sekali. Menambahkan mereka secara terprogram ke dalam viewgroup placeholder akan bekerja tanpa peringatan?
Rafael Nobre
Sepertinya masih berfungsi untuk saya, saya menukar mereka keluar dan masuk dari viewholder tidak masalah juga. Satu peringatan saya hanya melakukan ini di honeycomb tidak dengan kompatibilitas dengan Ice cream sandwich.
draksia
4

Saya telah menghadapi masalah yang sama, telah berjuang beberapa hari dengannya dan harus mengatakan bahwa cara paling mudah untuk mengatasinya saya menemukan ini adalah dengan menggunakan fragment.hide () / fragment.show () ketika tab dipilih / tidak dipilih ().

public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft)
{
    if (mFragment != null)
        ft.hide(mFragment);
}

Ketika rotasi layar terjadi, semua fragmen orangtua dan anak dihancurkan dengan benar.

Pendekatan ini juga memiliki satu keuntungan tambahan - menggunakan hide () / show () tidak menyebabkan tampilan fragmen kehilangan statusnya, jadi tidak perlu mengembalikan posisi gulir sebelumnya untuk ScrollViews misalnya.

Masalahnya adalah saya tidak tahu apakah itu benar untuk tidak melepaskan fragmen ketika mereka tidak terlihat. Saya pikir contoh resmi TabListener dirancang dengan pemikiran bahwa fragmen dapat digunakan kembali dan Anda tidak boleh mencemari memori mereka, namun, saya pikir jika Anda hanya memiliki beberapa tab dan Anda tahu bahwa pengguna akan sering berpindah di antaranya. akan sesuai untuk membuatnya melekat pada aktivitas saat ini.

Saya ingin mendengar komentar dari pengembang yang lebih berpengalaman.

ievgen
sumber
0

Jika Anda menemukan fragmen bersarang Anda tidak dihapus atau digandakan (mis. Saat Activity restart, on rotate screen) coba ubah:

transaction.add(R.id.placeholder, newFragment);

untuk

transaction.replace(R.id.placeholder, newFragment);

Jika di atas tidak membantu, coba:

Fragment f = getChildFragmentManager().findFragmentById(R.id.placeholder);

FragmentTransaction transaction = getChildFragmentManager().beginTransaction();

if (f == null) {
    Log.d(TAG, "onCreateView: fragment doesn't exist");
    newFragment= new MyFragmentType();
    transaction.add(R.id.placeholder, newFragment);
} else {
    Log.d(TAG, "onCreateView: fragment already exists");
    transaction.replace(R.id.placeholder, f);
}
transaction.commit();

Belajar di sini

Voy
sumber