Di android, fragmen (katakanlah FragA
) ditambahkan ke backstack dan fragmen lain (katakanlah FragB
) muncul di atas. Sekarang memukul kembali FragA
datang ke atas dan onCreateView()
dipanggil. Sekarang saya berada FragA
dalam kondisi tertentu sebelum FragB
didorong ke atasnya.
Pertanyaan saya adalah bagaimana cara mengembalikan FragA
ke keadaan sebelumnya? Apakah ada cara untuk menyimpan status (seperti katakan dalam Bundle) dan jika demikian, metode mana yang harus saya ganti?
android
android-fragments
pankajagarwal
sumber
sumber
onSaveInstanceState
hanya dipanggil jika aktivitas yang sesuai juga dimatikan.Fragmen
onSaveInstanceState(Bundle outState)
tidak akan pernah dipanggil kecuali aktivitas fragmen memanggilnya sendiri dan fragmen yang dilampirkan. Jadi metode ini tidak akan dipanggil hingga sesuatu (biasanya rotasi) memaksa aktivitasSaveInstanceState
dan memulihkannya nanti. Namun jika Anda hanya memiliki satu aktivitas dan sekumpulan besar fragmen di dalamnya (dengan penggunaan intensifreplace
) dan aplikasi hanya berjalan dalam satu orientasi, aktivitasonSaveInstanceState(Bundle outState)
mungkin tidak akan dipanggil untuk waktu yang lama.Saya tahu tiga solusi yang mungkin.
Pertama:
gunakan argumen fragmen untuk menyimpan data penting:
public class FragmentA extends Fragment { private static final String PERSISTENT_VARIABLE_BUNDLE_KEY = "persistentVariable"; private EditText persistentVariableEdit; public FragmentA() { setArguments(new Bundle()); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_a, null); persistentVariableEdit = (EditText) view.findViewById(R.id.editText); TextView proofTextView = (TextView) view.findViewById(R.id.textView); Bundle mySavedInstanceState = getArguments(); String persistentVariable = mySavedInstanceState.getString(PERSISTENT_VARIABLE_BUNDLE_KEY); proofTextView.setText(persistentVariable); view.findViewById(R.id.btnPushFragmentB).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { getFragmentManager() .beginTransaction() .replace(R.id.frameLayout, new FragmentB()) .addToBackStack(null) .commit(); } }); return view; } @Override public void onPause() { super.onPause(); String persistentVariable = persistentVariableEdit.getText().toString(); getArguments().putString(PERSISTENT_VARIABLE_BUNDLE_KEY, persistentVariable); } }
Cara kedua tetapi kurang saksama - pegang variabel dalam bentuk tunggal
Yang ketiga - jangan
replace()
memecah tetapiadd()
/show()
/hide()
mereka sebagai gantinya.sumber
Fragment.onSaveInstanceState()
tidak pernah dipanggil. Simpan saja data Anda sendiri ke argumen, termasuk item dalam tampilan daftar atau hanya ID mereka (jika Anda memiliki pengelola data terpusat lainnya). Tidak perlu menyimpan posisi tampilan daftar - yang disimpan dan dipulihkan secara otomatis.String persistentVariable = mySavedInstanceState.getString(PERSISTENT_VARIABLE_BUNDLE_KEY);
selalunull
. Apa masalahnya?getArguments()
PASTI cara yang harus dilakukan, termasuk fragmen bertingkat dalam ViewPager. Saya menggunakan 1 aktivitas dan menukar banyak fragmen masuk / keluar, dan ini bekerja dengan sempurna. Berikut adalah tes sederhana bagi Anda untuk memeriksa solusi yang diusulkan: 1) beralih dari Fragmen A ke Fragmen B; 2) ubah orientasi perangkat dua kali; 3) tekan tombol kembali pada perangkat.setArguments(new Bundle());
menimpa Bundel lama. Jadi pastikan Anda membuat fragmen hanya sekali, lalu gunakan instance ini daripada membuat yang baru setiap saat.Perhatikan bahwa jika Anda bekerja dengan Fragmen menggunakan ViewPager, itu cukup mudah. Anda hanya perlu memanggil metode ini:
setOffscreenPageLimit()
.Sesuai dengan dokumen:
Masalah serupa di sini
sumber
Cukup kembangkan View Anda sekali.
Contoh berikut:
public class AFragment extends Fragment { private View mRootView; @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if(mRootView==null){ mRootView = inflater.inflate(R.id.fragment_a, container, false); //...... } return mRootView; }
}
sumber
Fragment
mengacu pada tampilan akarnya tidak akan melakukan itu. Sementara referensi oleh beberapa elemen root GC akan, seperti properti statis global, variabel utas non-ui. SebuahFragment
instance bukanlah GC-root, sehingga dapat dikumpulkan sampahnya. Begitu juga tampilan akarnya.Saya bekerja dengan masalah yang sangat mirip dengan ini. Karena saya tahu saya akan sering kembali ke fragmen sebelumnya, saya memeriksa untuk melihat apakah fragmen
.isAdded()
itu benar, dan jika demikian, daripada melakukantransaction.replace()
saya hanya melakukan atransaction.show()
. Hal ini mencegah fragmen dibuat ulang jika sudah ada di stack - tidak perlu menyimpan status.Fragment target = <my fragment>; FragmentTransaction transaction = getFragmentManager().beginTransaction(); transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); if(target.isAdded()) { transaction.show(target); } else { transaction.addToBackStack(button_id + "stack_item"); transaction.replace(R.id.page_fragment, target); } transaction.commit();
Hal lain yang perlu diingat adalah meskipun ini mempertahankan urutan alami untuk fragmen itu sendiri, Anda mungkin masih perlu menangani aktivitas itu sendiri yang dimusnahkan dan dibuat ulang saat orientasi (config) berubah. Untuk menyiasatinya di AndroidManifest.xml untuk node Anda:
android:configChanges="orientation|screenSize"
Di Android 3.0 dan lebih tinggi,
screenSize
tampaknya diperlukan.Semoga berhasil
sumber
android:configChanges=everythinYouCanThinkOf|moreThingsYouFoundOnTheInternets
dalam manifes Anda. Alih-alih pelajari cara menyimpan dan memulihkan status, misalnya dari sini: speakerdeck.com/cyrilmottier/…Solusi Terbaik yang saya temukan ada di bawah:
onSavedInstanceState (): selalu dipanggil di dalam fragmen saat aktivitas akan dimatikan (Pindahkan aktivitas dari satu ke yang lain atau konfigurasi berubah). Jadi jika kita memanggil banyak fragmen pada aktivitas yang sama maka kita harus menggunakan pendekatan berikut:
Gunakan OnDestroyView () dari fragmen dan simpan seluruh objek di dalam metode itu. Kemudian OnActivityCreated (): Periksa apakah objek null atau tidak (Karena metode ini memanggil setiap waktu). Sekarang pulihkan status objek di sini.
Ini selalu berhasil!
sumber
jika Anda menangani perubahan konfigurasi dalam aktivitas fragmen Anda yang ditentukan dalam manifes android seperti ini
<activity android:name=".courses.posts.EditPostActivity" android:configChanges="keyboardHidden|orientation" android:screenOrientation="unspecified" />
maka
onSaveInstanceState
dari fragmen tidak akan dipanggil dansavedInstanceState
objek akan selalu null.sumber
Saya rasa
onSaveInstanceState
itu bukan solusi yang baik. itu hanya digunakan untuk aktivitas yang telah dimusnahkan.Dari android 3.0, Fragmen telah dikelola oleh FragmentManager, kondisinya adalah: satu fragmen manny pemetaan aktivitas, ketika fragmen ditambahkan (bukan menggantikan: itu akan dibuat ulang) di backStack, tampilan akan dihancurkan. ketika kembali ke yang terakhir, itu akan ditampilkan seperti sebelumnya.
Jadi menurut saya fragmentManger dan transaction cukup baik untuk menanganinya.
sumber
Saya menggunakan pendekatan hybrid untuk fragmen yang berisi tampilan daftar. Tampaknya berhasil karena saya tidak mengganti fragmen saat ini, melainkan menambahkan fragmen baru dan menyembunyikan yang sekarang. Saya memiliki metode berikut dalam aktivitas yang menghosting fragmen saya:
public void addFragment(Fragment currentFragment, Fragment targetFragment, String tag) { FragmentManager fragmentManager = getSupportFragmentManager(); FragmentTransaction transaction = fragmentManager.beginTransaction(); transaction.setCustomAnimations(0,0,0,0); transaction.hide(currentFragment); // use a fragment tag, so that later on we can find the currently displayed fragment transaction.add(R.id.frame_layout, targetFragment, tag) .addToBackStack(tag) .commit(); }
Saya menggunakan metode ini dalam fragmen saya (berisi tampilan daftar) setiap kali item daftar diklik / diketuk (dan karenanya saya perlu meluncurkan / menampilkan fragmen detail):
FragmentManager fragmentManager = getActivity().getSupportFragmentManager(); SearchFragment currentFragment = (SearchFragment) fragmentManager.findFragmentByTag(getFragmentTags()[0]); DetailsFragment detailsFragment = DetailsFragment.newInstance("some object containing some details"); ((MainActivity) getActivity()).addFragment(currentFragment, detailsFragment, "Details");
getFragmentTags()
mengembalikan larik string yang saya gunakan sebagai tag untuk fragmen berbeda ketika saya menambahkan fragmen baru (lihattransaction.add
metode dalamaddFragment
metode di atas).Dalam fragmen yang berisi tampilan daftar, saya melakukan ini dalam metode onPause ():
@Override public void onPause() { // keep the list view's state in memory ("save" it) // before adding a new fragment or replacing current fragment with a new one ListView lv = (ListView) getActivity().findViewById(R.id.listView); mListViewState = lv.onSaveInstanceState(); super.onPause(); }
Kemudian di onCreateView dari fragmen (sebenarnya dalam metode yang dipanggil di onCreateView), saya memulihkan status:
// Restore previous state (including selected item index and scroll position) if(mListViewState != null) { Log.d(TAG, "Restoring the listview's state."); lv.onRestoreInstanceState(mListViewState); }
sumber
Pada akhirnya setelah mencoba banyak solusi rumit ini karena saya hanya perlu menyimpan / memulihkan satu nilai di Fragmen saya (konten EditText), dan meskipun itu mungkin bukan solusi paling elegan, membuat SharedPreference dan menyimpan status saya di sana bekerja untuk saya
sumber
Cara sederhana untuk menyimpan nilai kolom dalam fragmen berbeda dalam suatu aktivitas
Buat Instance dari fragmen dan tambahkan alih-alih mengganti dan menghapus
FragA fa= new FragA(); FragB fb= new FragB(); FragC fc= new FragB(); fragmentManager = getSupportFragmentManager(); fragmentTransaction = fragmentManager.beginTransaction(); fragmentTransaction.add(R.id.fragmnt_container, fa); fragmentTransaction.add(R.id.fragmnt_container, fb); fragmentTransaction.add(R.id.fragmnt_container, fc); fragmentTransaction.show(fa); fragmentTransaction.hide(fb); fragmentTransaction.hide(fc); fragmentTransaction.commit();
Kemudian cukup tampilkan dan sembunyikan fragmen alih-alih menambahkan dan menghapusnya lagi
fragmentTransaction = fragmentManager.beginTransaction(); fragmentTransaction.hide(fa); fragmentTransaction.show(fb); fragmentTransaction.hide(fc); fragmentTransaction.commit()
;
sumber
private ViewPager viewPager; viewPager = (ViewPager) findViewById(R.id.pager); mAdapter = new TabsPagerAdapter(getSupportFragmentManager()); viewPager.setAdapter(mAdapter); viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageSelected(int position) { // on changing the page // make respected tab selected actionBar.setSelectedNavigationItem(position); } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { } @Override public void onPageScrollStateChanged(int arg0) { } }); } @Override public void onTabReselected(Tab tab, FragmentTransaction ft) { } @Override public void onTabSelected(Tab tab, FragmentTransaction ft) { // on tab selected // show respected fragment view viewPager.setCurrentItem(tab.getPosition()); } @Override public void onTabUnselected(Tab tab, FragmentTransaction ft) { }
sumber