Dengan Android 4.2, pustaka dukungan mendapat dukungan untuk fragmen bersarang, lihat di sini . Saya telah bermain-main dengannya dan menemukan perilaku / bug yang menarik terkait back stack dan getChildFragmentManager () . Saat menggunakan getChildFragmentManager () dan addToBackStack (Nama string), dengan menekan tombol kembali, sistem tidak menjalankan back-stack ke fragmen sebelumnya. Sebaliknya, saat menggunakan getFragmentManager () dan addToBackStack (Nama string), dengan menekan tombol kembali, sistem akan kembali ke fragmen sebelumnya.
Bagi saya, perilaku ini tidak terduga. Dengan menekan tombol kembali pada perangkat saya, saya berharap bahwa fragmen terakhir yang ditambahkan ke tumpukan belakang akan muncul, bahkan jika fragmen ditambahkan ke tumpukan belakang di pengelola fragmen anak-anak.
Apakah perilaku ini benar? Apakah perilaku ini bug? Apakah ada solusi untuk masalah ini?
contoh kode dengan getChildFragmentManager ():
public class FragmentceptionActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);
final FrameLayout wrapper1 = new FrameLayout(this);
wrapper1.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper1.setId(1);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 0;
final TextView text = new TextView(this);
text.setLayoutParams(params);
text.setText("fragment 1");
wrapper1.addView(text);
setContentView(wrapper1);
getSupportFragmentManager().beginTransaction().addToBackStack(null)
.add(1, new Fragment1()).commit();
}
public class Fragment1 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper2 = new FrameLayout(getActivity());
wrapper2.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper2.setId(2);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 100;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 2");
wrapper2.addView(text);
return wrapper2;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getFragmentManager().beginTransaction().addToBackStack(null)
.add(2, new Fragment2()).commit();
}
}
public class Fragment2 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper3 = new FrameLayout(getActivity());
wrapper3.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper3.setId(3);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 200;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 3");
wrapper3.addView(text);
return wrapper3;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getChildFragmentManager().beginTransaction().addToBackStack(null)
.add(3, new Fragment3()).commit();
}
}
public class Fragment3 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper4 = new FrameLayout(getActivity());
wrapper4.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper4.setId(4);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 300;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 4");
wrapper4.addView(text);
return wrapper4;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getChildFragmentManager().beginTransaction().addToBackStack(null)
.add(4, new Fragment4()).commit();
}
}
public class Fragment4 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper5 = new FrameLayout(getActivity());
wrapper5.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper5.setId(5);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 400;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 5");
wrapper5.addView(text);
return wrapper5;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
}
}
kode contoh dengan getFragmentManager ():
public class FragmentceptionActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);
final FrameLayout wrapper1 = new FrameLayout(this);
wrapper1.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper1.setId(1);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 0;
final TextView text = new TextView(this);
text.setLayoutParams(params);
text.setText("fragment 1");
wrapper1.addView(text);
setContentView(wrapper1);
getSupportFragmentManager().beginTransaction().addToBackStack(null)
.add(1, new Fragment1()).commit();
}
public class Fragment1 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper2 = new FrameLayout(getActivity());
wrapper2.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper2.setId(2);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 100;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 2");
wrapper2.addView(text);
return wrapper2;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getFragmentManager().beginTransaction().addToBackStack(null)
.add(2, new Fragment2()).commit();
}
}
public class Fragment2 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper3 = new FrameLayout(getActivity());
wrapper3.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper3.setId(3);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 200;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 3");
wrapper3.addView(text);
return wrapper3;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getFragmentManager().beginTransaction().addToBackStack(null)
.add(3, new Fragment3()).commit();
}
}
public class Fragment3 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper4 = new FrameLayout(getActivity());
wrapper4.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper4.setId(4);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 300;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 4");
wrapper4.addView(text);
return wrapper4;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getFragmentManager().beginTransaction().addToBackStack(null)
.add(4, new Fragment4()).commit();
}
}
public class Fragment4 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper5 = new FrameLayout(getActivity());
wrapper5.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper5.setId(5);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 400;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 5");
wrapper5.addView(text);
return wrapper5;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
}
}
Jawaban:
Solusi ini mungkin versi yang lebih baik dari jawaban @Sean:
Sekali lagi, saya menyiapkan solusi ini berdasarkan jawaban @Sean di atas.
Seperti yang dikatakan @ AZ13, solusi ini hanya dapat dilakukan dalam situasi fragmen turunan satu level. Dalam kasus beberapa tingkat fragmen, pekerjaan menjadi sedikit rumit, jadi saya sarankan mencoba solusi ini hanya kasus yang layak saya katakan. =)
Catatan: Karena
getFragments
metode sekarang menjadi metode pribadi, solusi ini tidak akan berfungsi. Anda dapat memeriksa komentar untuk menemukan tautan yang menyarankan solusi tentang situasi ini.sumber
getFragments
sekarang menjadi publik.Sepertinya bug. Lihat di: http://code.google.com/p/android/issues/detail?id=40323
Untuk solusi yang berhasil saya gunakan (seperti yang disarankan dalam komentar):
sumber
Jawaban sebenarnya untuk pertanyaan ini ada di fungsi Fragment Transaction yang disebut setPrimaryNavigationFragment.
Anda harus menyetel fungsi ini pada fragmen induk awal saat aktivitas menambahkannya. Saya memiliki fungsi replaceFragment di dalam aktivitas saya yang terlihat seperti ini:
Ini memberi Anda perilaku yang sama seperti jika Anda mengklik kembali dari Fragmen B biasa kembali ke Fragmen A, kecuali sekarang ini juga pada fragmen turunan!
sumber
Solusi ini mungkin versi yang lebih baik dari jawaban @ismailarilik:
Versi Fragmen Bersarang
sumber
SupportFragmentManager
dan sebagai gantinya hanya StandarFragmentManager
?Dengan jawaban ini, ia akan menangani pemeriksaan ulang rekursif dan memberikan setiap fragmen kesempatan untuk mengganti perilaku default. Ini berarti Anda dapat memiliki fragmen yang menghosting ViewPager melakukan sesuatu yang istimewa seperti menggulir ke halaman itu sebagai back-stack, atau menggulir ke halaman beranda dan kemudian kembali tekan keluar berikutnya.
Tambahkan ini ke Aktivitas Anda yang memperluas AppCompatActivity.
Tambahkan ini ke BaseFragment Anda atau kelas tempat semua fragmen Anda mewarisi.
sumber
Kode ini akan menavigasi pohon pengelola fragmen dan mengembalikan yang terakhir ditambahkan yang memiliki fragmen apa pun yang dapat dikeluarkan dari tumpukan:
Panggil metode:
sumber
Alasannya adalah Aktivitas Anda berasal dari FragmentActivity, yang menangani penekanan tombol KEMBALI (lihat baris 173 dari FragmentActivity .
Dalam aplikasi kita, saya menggunakan ViewPager (dengan fragmen) dan setiap fragmen dapat memiliki fragmen bersarang. Cara saya menangani ini adalah dengan:
void onBackKeyPressed()
Perhatikan juga, yang saya gunakan
getChildFragmentManager()
dalam fragmen untuk menyarangkan fragmen dengan benar. Pembahasan dan penjelasannya bisa kalian lihat di postingan android-developers ini .sumber
jika Anda menggunakan fragmen dalam sebuah fragmen, gunakan
getChildFragmentManager()
jika Anda menggunakan fragmen anak dan menggantinya gunakan
getParentFragmentManager()
jika keduanya tidak berfungsi untuk Anda, coba ini
getActivity().getSupportFragmentManager()
sumber
saya bisa menangani tumpukan belakang fragmen dengan menambahkan ke fragmen induk metode ini pada metode onCreate View () dan meneruskan tampilan root.
Metode isEnableFragmentBackStack () adalah tanda boolean untuk mengetahui saat saya berada di fragmen utama atau yang berikutnya.
Pastikan bahwa ketika Anda mengkomit fragmen yang perlu memiliki tumpukan, Anda harus menambahkan Metode addToBackstack.
sumber
Solusi ini mungkin lebih baik, karena memeriksa semua level fragmen bersarang:
dalam aktivitas
onBackPressed
Anda, Anda dapat menyebutnya seperti ini:sumber
Terima kasih kepada semua orang atas bantuan mereka, ini (versi tweak) berfungsi untuk saya:
CATATAN: Anda mungkin juga ingin menyetel warna latar belakang dari fragmen bersarang ini ke warna latar belakang jendela tema aplikasi, karena secara default transparan. Agak di luar cakupan pertanyaan ini, tetapi diselesaikan dengan menyelesaikan atribut android.R.attr.windowBackground, dan menyetel latar belakang tampilan Fragmen ke ID sumber daya tersebut.
sumber
Lebih dari 5 tahun dan masalah ini masih relevan. Jika Anda tidak ingin menggunakan fragmentManager.getFragments () karena batasannya. Perluas dan gunakan kelas di bawah ini:
NestedFragmentActivity.java
NestedFragment.java
Penjelasan:
Setiap aktivitas dan fragmen yang diperluas dari kelas-kelas di atas mengelola back-stack mereka sendiri untuk masing-masing anak mereka, anak dari anak-anak mereka, dan seterusnya. Backstack hanyalah catatan tag / id "fragmen aktif". Jadi peringatannya adalah selalu memberikan tag dan / atau id untuk fragmen Anda.
Saat menambahkan ke backstack di childFragmentManager, Anda juga perlu memanggil "addToParentBackStack ()". Ini memastikan bahwa tag / id fragmen ditambahkan di fragmen / aktivitas induk untuk muncul nanti.
Contoh:
sumber
Saya telah menerapkannya dengan benar jika ada yang tidak menemukan jawaban sampai sekarang
cukup tambahkan metode ini pada fragmen bersarang anak Anda
sumber
Saya memecahkan masalah ini dengan menyimpan fragmen yang saat ini dibuka di properti aktivitas. Kemudian saya mengganti metode
Beginilah cara saya menambahkan fragmen anak ke dalam fragmen yang saat ini dibuka dari dalam dirinya sendiri.
Ini adalah layout xml dari fragmen induk dan fragmen yang ditambahkan
sumber
Setelah mengamati beberapa solusi yang disajikan di sini, saya menemukan bahwa untuk memungkinkan fleksibilitas & kontrol untuk fragmen induk seperti saat memunculkan tumpukan atau kapan tindakan mundur harus diabaikan olehnya, saya lebih suka menggunakan implementasi seperti itu:
Mendefinisikan antarmuka "ParentFragment":
}
Mengganti "onBackPressed" di aktivitas induk (atau di BaseActivity):
Dan kemudian biarkan fragmen induk menangani sesuai keinginannya, misalnya:
Hal ini memungkinkan untuk menghindari pemikiran bahwa ada beberapa fragmen turunan yang sah untuk dihapus saat menggunakan halaman tampilan misalnya (dan jumlah entri back stack> 0).
sumber
Jika Anda memiliki
DialogFragment
yang pada gilirannya memiliki fragmen bersarang, 'solusinya' sedikit berbeda. Alih-alih menyetelonKeyListener
kerootView
, Anda harus melakukannya denganDialog
. Anda juga akan menyiapkanDialogInterface.OnKeyListener
dan bukan yangView
satu. Tentu ingataddToBackStack
!Inilah yang harus Anda lakukan dalam onCreateDialog
sumber
Dialog
(bukanDialogFragment
) dan pendengar yang Anda gunakan adalahDialogInterface.OnKeyListener
- kode berfungsi dan lengkap (dan sedang digunakan). Mengapa Anda tidak memberikan situasi Anda dan mungkin saya dapat membantu.untuk ChildFragments, ini berfungsi ..
sumber