INFO TERBARU:
saya telah mempersempit masalah saya menjadi masalah dengan fragmentManager yang mempertahankan contoh fragmen lama dan viewpager saya tidak sinkron dengan FragmentManager saya. Lihat masalah ini ... http://code.google.com/p/android/issues/detail?id=19211#makechanges . Saya masih tidak tahu bagaimana menyelesaikan ini. Ada saran ...
Saya telah mencoba men-debug ini untuk waktu yang lama dan bantuan apa pun akan sangat kami hargai. Saya menggunakan FragmentPagerAdapter yang menerima daftar fragmen seperti ini:
List<Fragment> fragments = new Vector<Fragment>();
fragments.add(Fragment.instantiate(this, Fragment1.class.getName()));
...
new PagerAdapter(getSupportFragmentManager(), fragments);
Implementasinya standar. Saya menggunakan pustaka komputasi ActionBarSherlock dan v4 untuk Fragmen.
Masalah saya adalah bahwa setelah meninggalkan aplikasi dan membuka beberapa aplikasi lain dan kembali, fragmen kehilangan referensi kembali ke FragmentActivity (mis. getActivity() == null
). Saya tidak tahu mengapa ini terjadi. Saya mencoba mengatur secara manual setRetainInstance(true);
tetapi ini tidak membantu. Saya membayangkan bahwa ini terjadi ketika FragmentActivity saya dihancurkan, namun ini masih terjadi jika saya membuka aplikasi sebelum saya mendapatkan pesan log. Apakah ada ide?
@Override
protected void onDestroy(){
Log.w(TAG, "DESTROYDESTROYDESTROYDESTROYDESTROYDESTROYDESTROY");
super.onDestroy();
}
Adaptor:
public class PagerAdapter extends FragmentPagerAdapter {
private List<Fragment> fragments;
public PagerAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
this.fragments = fragments;
}
@Override
public Fragment getItem(int position) {
return this.fragments.get(position);
}
@Override
public int getCount() {
return this.fragments.size();
}
}
Salah satu Fragmen saya dilucuti tetapi saya membayar semuanya itu dilucuti dan masih tidak berfungsi ...
public class MyFragment extends Fragment implements MyFragmentInterface, OnScrollListener {
...
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
handler = new Handler();
setHasOptionsMenu(true);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
Log.w(TAG,"ATTACHATTACHATTACHATTACHATTACH");
context = activity;
if(context== null){
Log.e("IS NULL", "NULLNULLNULLNULLNULLNULLNULLNULLNULLNULLNULL");
}else{
Log.d("IS NOT NULL", "NOTNOTNOTNOTNOTNOTNOTNOT");
}
}
@Override
public void onActivityCreated(Bundle savedState) {
super.onActivityCreated(savedState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.my_fragment,container, false);
return v;
}
@Override
public void onResume(){
super.onResume();
}
private void callService(){
// do not call another service is already running
if(startLoad || !canSet) return;
// set flag
startLoad = true;
canSet = false;
// show the bottom spinner
addFooter();
Intent intent = new Intent(context, MyService.class);
intent.putExtra(MyService.STATUS_RECEIVER, resultReceiver);
context.startService(intent);
}
private ResultReceiver resultReceiver = new ResultReceiver(null) {
@Override
protected void onReceiveResult(int resultCode, final Bundle resultData) {
boolean isSet = false;
if(resultData!=null)
if(resultData.containsKey(MyService.STATUS_FINISHED_GET)){
if(resultData.getBoolean(MyService.STATUS_FINISHED_GET)){
removeFooter();
startLoad = false;
isSet = true;
}
}
switch(resultCode){
case MyService.STATUS_FINISHED:
stopSpinning();
break;
case SyncService.STATUS_RUNNING:
break;
case SyncService.STATUS_ERROR:
break;
}
}
};
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.clear();
inflater.inflate(R.menu.activity, menu);
}
@Override
public void onPause(){
super.onPause();
}
public void onScroll(AbsListView arg0, int firstVisible, int visibleCount, int totalCount) {
boolean loadMore = /* maybe add a padding */
firstVisible + visibleCount >= totalCount;
boolean away = firstVisible+ visibleCount <= totalCount - visibleCount;
if(away){
// startLoad can now be set again
canSet = true;
}
if(loadMore)
}
public void onScrollStateChanged(AbsListView arg0, int state) {
switch(state){
case OnScrollListener.SCROLL_STATE_FLING:
adapter.setLoad(false);
lastState = OnScrollListener.SCROLL_STATE_FLING;
break;
case OnScrollListener.SCROLL_STATE_IDLE:
adapter.setLoad(true);
if(lastState == SCROLL_STATE_FLING){
// load the images on screen
}
lastState = OnScrollListener.SCROLL_STATE_IDLE;
break;
case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
adapter.setLoad(true);
if(lastState == SCROLL_STATE_FLING){
// load the images on screen
}
lastState = OnScrollListener.SCROLL_STATE_TOUCH_SCROLL;
break;
}
}
@Override
public void onDetach(){
super.onDetach();
if(this.adapter!=null)
this.adapter.clearContext();
Log.w(TAG, "DETACHEDDETACHEDDETACHEDDETACHEDDETACHEDDETACHED");
}
public void update(final int id, String name) {
if(name!=null){
getActivity().getSupportActionBar().setTitle(name);
}
}
}
Metode update dipanggil saat pengguna berinteraksi dengan fragmen berbeda dan getActivity menampilkan null. Inilah metode yang dipanggil oleh fragmen lain ...
((MyFragment) pagerAdapter.getItem(1)).update(id, name);
Saya percaya bahwa ketika aplikasi dihancurkan kemudian dibuat lagi alih-alih hanya memulai aplikasi ke fragmen default, aplikasi dimulai dan kemudian viewpager menavigasi ke halaman terakhir yang diketahui. Ini tampak aneh, bukankah seharusnya aplikasi memuat ke fragmen default?
sumber
Jawaban:
Anda mengalami masalah karena Anda membuat instance dan menyimpan referensi ke fragmen Anda di luar
PagerAdapter.getItem
, dan mencoba menggunakan referensi tersebut secara terpisah dari ViewPager. Seperti yang dikatakan Seraph, Anda memiliki jaminan bahwa sebuah fragmen telah dibuat / ditambahkan di ViewPager pada waktu tertentu - ini harus dianggap sebagai detail implementasi. ViewPager lambat memuat halamannya; secara default hanya memuat halaman saat ini, dan satu ke kiri dan kanan.Jika Anda meletakkan aplikasi Anda di latar belakang, fragmen yang telah ditambahkan ke pengelola fragmen akan disimpan secara otomatis. Meskipun aplikasi Anda mati, informasi ini dipulihkan saat Anda meluncurkan kembali aplikasi Anda.
Sekarang pertimbangkan bahwa Anda telah melihat beberapa halaman, Fragmen A, B dan C. Anda tahu bahwa ini telah ditambahkan ke pengelola fragmen. Karena Anda sedang menggunakan
FragmentPagerAdapter
dan tidakFragmentStatePagerAdapter
, fragmen ini masih akan ditambahkan (tetapi berpotensi terlepas) saat Anda menggulir ke halaman lain.Pertimbangkan bahwa Anda kemudian membuat latar belakang aplikasi Anda, dan kemudian terhenti. Saat Anda kembali, Android akan mengingat bahwa Anda dulu memiliki Fragmen A, B, dan C di pengelola fragmen sehingga ia membuatnya kembali untuk Anda dan kemudian menambahkannya. Namun, yang ditambahkan ke pengelola fragmen sekarang BUKAN yang Anda miliki di daftar fragmen di Aktivitas Anda.
FragmentPagerAdapter tidak akan mencoba memanggil
getPosition
jika sudah ada fragmen yang ditambahkan untuk posisi halaman tersebut. Faktanya, karena fragmen yang dibuat ulang oleh Android tidak akan pernah dihapus, Anda tidak memiliki harapan untuk menggantinya dengan panggilan kegetPosition
. Mendapatkan pegangan di atasnya juga cukup sulit untuk mendapatkan referensi ke sana karena ditambahkan dengan tag yang tidak Anda kenal. Ini adalah desain; Anda tidak disarankan untuk mengotak-atik fragmen yang dikelola view pager. Anda harus melakukan semua tindakan Anda dalam sebuah fragmen, berkomunikasi dengan aktivitas tersebut, dan meminta untuk beralih ke halaman tertentu, jika perlu.Sekarang, kembali ke masalah Anda dengan aktivitas yang hilang. Memanggil
pagerAdapter.getItem(1)).update(id, name)
setelah semua ini terjadi akan mengembalikan Anda fragmen dalam daftar Anda, yang belum ditambahkan ke pengelola fragmen , sehingga tidak akan memiliki referensi Aktivitas. Saya menyarankan metode pembaruan Anda harus memodifikasi beberapa struktur data bersama (mungkin dikelola oleh aktivitas), dan kemudian ketika Anda pindah ke halaman tertentu, metode ini dapat menggambar sendiri berdasarkan data yang diperbarui ini.sumber
instantiateItem
dan Anda seharusnya melakukannya dalamonCreate
aktivitas Anda. lihat detailnya di sini: stackoverflow.com/questions/14035090/…Saya menemukan solusi sederhana yang berhasil untuk saya.
Jadikan adaptor fragmen Anda memperluas FragmentStatePagerAdapter sebagai ganti FragmentPagerAdapter dan ganti metode onSave untuk mengembalikan null
Ini mencegah android membuat ulang fragmen
Suatu hari kemudian saya menemukan solusi lain yang lebih baik.
Panggil
setRetainInstance(true)
semua fragmen Anda dan simpan referensi ke sana di suatu tempat. Saya melakukannya di variabel statis dalam aktivitas saya, karena dideklarasikan sebagai singleTask dan fragmen bisa tetap sama sepanjang waktu.Dengan cara ini android tidak membuat ulang fragmen tetapi menggunakan instance yang sama.
sumber
setRetainInstance(true)
denganFragmentPagerAdapter
. Semuanya bekerja dengan baik. Namun saat saya memutar perangkat, adaptor masih memiliki fragmen tetapi fragmen tidak ditampilkan. Metode siklus proses fragmen juga tidak dipanggil. Adakah yang bisa membantu?Saya memecahkan masalah ini dengan mengakses fragmen saya secara langsung melalui FragmentManager, bukan melalui FragmentPagerAdapter seperti itu. Pertama, saya perlu mencari tahu tag dari fragmen yang dibuat secara otomatis oleh FragmentPagerAdapter ...
Kemudian saya hanya mendapatkan referensi ke fragmen itu dan melakukan apa yang saya butuhkan begitu ...
Di dalam fragmen saya, saya mengatur
setRetainInstance(false);
agar saya dapat menambahkan nilai secara manual ke bundel storedInstanceState.dan kemudian di OnCreate saya mengambil kunci itu dan memulihkan status fragmen seperlunya. Solusi mudah yang sulit (setidaknya bagi saya) untuk dipikirkan.
sumber
FragmentByTag
di ViewPager.Solusi teruji kerja global.
getSupportFragmentManager()
menyimpan referensi null beberapa kali dan halaman Tampilan tidak membuat yang baru karena menemukan referensi ke fragmen yang sama. Jadi untukgetChildFragmentManager()
mengatasi penggunaan ini memecahkan masalah dengan cara yang sederhana.Jangan lakukan ini:
new PagerAdapter(getSupportFragmentManager(), fragments);
Melakukan hal ini:
new PagerAdapter(getChildFragmentManager() , fragments);
sumber
FragmentStatePagerAdapter(activity!!.supportFragmentManager)
menjadi lebih mudah untuk dilihatFragmentStatePagerAdapter(childFragmentManager)
:)Jangan mencoba berinteraksi di antara fragmen di ViewPager. Anda tidak dapat menjamin bahwa fragmen lain terpasang atau bahkan ada. Alih-alih mengubah judul bilah tindakan dari fragmen, Anda dapat melakukannya dari aktivitas Anda. Gunakan pola antarmuka standar untuk ini:
sumber
Anda dapat menghapus fragmen saat menghancurkan viewpager, dalam kasus saya, saya menghapusnya
onDestroyView()
dari fragmen saya:sumber
ViewPager
juga didasarkan padachildFragmentManager
adaptor (bukanfragmentManager
). Varian lain juga berfungsi: jangan gunakanonDestroyView
, tetapi hapus fragmen turunan sebelumViewPager
pembuatan adaptor.Setelah beberapa jam mencari masalah serupa, saya pikir ada solusi lain. Yang ini setidaknya berhasil untuk saya dan saya hanya perlu mengubah beberapa baris.
Ini adalah masalah yang saya hadapi, saya memiliki aktivitas dengan halaman tampilan yang menggunakan FragmentStatePagerAdapter dengan dua Fragmen. Semuanya berfungsi dengan baik sampai saya memaksa aktivitas untuk dihancurkan (opsi pengembang) atau saya memutar layar. Saya menyimpan referensi ke dua fragmen setelah dibuat di dalam metode getItem.
Pada titik itu, aktivitas akan dibuat lagi dan semuanya berfungsi dengan baik pada titik ini, tetapi saya telah kehilangan referensi ke fragmetns saya karena getItem tidak dipanggil lagi.
Beginilah cara saya memperbaiki masalah itu, di dalam FragmentStatePagerAdapter:
Anda tidak akan mendapatkan panggilan di getItem lagi jika adaptor sudah memiliki referensi ke dalamnya secara internal, dan Anda tidak boleh mengubahnya. Sebagai gantinya Anda bisa mendapatkan fragmen yang sedang digunakan dengan melihat metode lain ini instantiateItem () yang akan dipanggil untuk setiap fragmen Anda.
Semoga bisa membantu siapa saja.
sumber
Karena FragmentManager akan menangani pemulihan Fragmen untuk Anda segera setelah metode onResume () dipanggil, saya memiliki panggilan fragmen ke aktivitas dan menambahkan dirinya sendiri ke daftar. Dalam contoh saya, saya menyimpan semua ini dalam implementasi PagerAdapter saya. Setiap fragmen mengetahui posisinya karena ditambahkan ke argumen fragmen saat pembuatan. Sekarang setiap kali saya perlu memanipulasi fragmen pada indeks tertentu, yang harus saya lakukan adalah menggunakan daftar dari adaptor saya.
Berikut ini adalah contoh Adaptor untuk ViewPager kustom yang akan mengembangkan fragmen saat bergerak menjadi fokus, dan menurunkan skalanya saat bergerak keluar dari fokus. Selain kelas Adapter dan Fragment yang saya miliki di sini, yang Anda butuhkan hanyalah agar aktivitas induk dapat mereferensikan variabel adaptor dan Anda sudah disetel.
Adaptor
Pecahan
sumber
Solusi saya: Saya menetapkan hampir setiap Tampilan sebagai
static
. Sekarang aplikasi saya berinteraksi dengan sempurna. Mampu memanggil metode statis dari mana-mana mungkin bukan gaya yang baik, tetapi mengapa harus bermain-main dengan kode yang tidak berfungsi? Saya membaca banyak pertanyaan dan jawaban mereka di SO dan tidak ada solusi yang membawa kesuksesan (bagi saya).Saya tahu itu dapat membocorkan memori, dan membuang tumpukan, dan kode saya tidak akan cocok untuk proyek lain, tetapi saya tidak merasa takut tentang ini - Saya menguji aplikasi pada perangkat dan kondisi yang berbeda, tidak ada masalah sama sekali, Android Platform sepertinya bisa menangani ini. UI disegarkan setiap detik dan bahkan pada perangkat S2 ICS (4.0.3) aplikasi mampu menangani ribuan penanda geografis.
sumber
Saya menghadapi masalah yang sama tetapi ViewPager saya berada di dalam TopFragment yang membuat dan menyetel adaptor menggunakan
setAdapter(new FragmentPagerAdapter(getChildFragmentManager()))
.Saya memperbaiki masalah ini dengan
onAttachFragment(Fragment childFragment)
mengganti di TopFragment seperti ini:Seperti yang sudah diketahui (lihat jawaban di atas), saat childFragmentManager membuat ulang dirinya sendiri, ia juga membuat fragmen yang ada di dalam viewPager.
Bagian pentingnya adalah setelah itu, dia memanggil onAttachFragment dan sekarang kita memiliki referensi ke fragmen baru yang dibuat ulang!
Semoga ini akan membantu siapa pun yang mendapatkan Q lama seperti saya :)
sumber
Saya memecahkan masalah dengan menyimpan fragmen di SparceArray:
sumber
Asal kamu tahu...
Menambah litani kesengsaraan dengan kelas-kelas ini, ada bug yang cukup menarik yang patut dibagikan.
Saya menggunakan ViewPager untuk menavigasi pohon item (pilih item dan view pager menganimasikan bergulir ke kanan, dan cabang berikutnya muncul, menavigasi kembali, dan ViewPager bergulir ke arah yang berlawanan untuk kembali ke node sebelumnya) .
Masalahnya muncul saat saya mendorong dan mengeluarkan fragmen dari akhir FragmentStatePagerAdapter. Cukup pintar untuk memperhatikan bahwa item berubah, dan cukup pintar untuk membuat dan mengganti fragmen ketika item telah berubah. Namun tidak cukup pintar untuk membuang status fragmen, atau cukup pintar untuk memangkas status fragmen yang disimpan secara internal saat ukuran adaptor berubah. Jadi, ketika Anda meletuskan sebuah item, dan mendorong yang baru ke bagian akhir, fragmen untuk item baru mendapatkan status tersimpan dari fragmen untuk item lama, yang menyebabkan kerusakan mutlak dalam kode saya. Fragmen saya membawa data yang mungkin memerlukan banyak pekerjaan untuk diambil ulang dari internet, jadi tidak menyimpan status bukanlah pilihan.
Saya tidak memiliki solusi yang bersih. Saya menggunakan sesuatu seperti ini:
Solusi yang tidak sempurna karena instance fragmen baru masih mendapatkan SaveState Bundle, tetapi setidaknya tidak membawa data usang.
sumber
Karena orang tidak cenderung membaca komentar, berikut adalah jawaban yang sebagian besar meniru apa yang saya tulis di sini :
akar penyebab masalah ini adalah kenyataan bahwa sistem android tidak memanggil
getItem
untuk mendapatkan fragmen yang sebenarnya ditampilkan, tetapiinstantiateItem
. Metode ini pertama kali mencoba mencari dan menggunakan kembali instance fragmen untuk tab tertentu diFragmentManager
. Hanya jika pencarian ini gagal (yang terjadi hanya pertama kali saatFragmentManager
baru dibuat) makagetItem
akan dipanggil. Itu untuk alasan yang jelas untuk tidak membuat ulang fragmen (yang mungkin berat) misalnya setiap kali pengguna memutar perangkatnya.Untuk mengatasi ini, alih-alih membuat fragmen
Fragment.instantiate
dalam aktivitas Anda, Anda harus melakukannya denganpagerAdapter.instantiateItem
dan semua panggilan ini harus diapit olehstartUpdate/finishUpdate
panggilan metode yang masing-masing memulai / melakukan transaksi fragmen.getItem
harus menjadi tempat di mana fragmen benar-benar dibuat menggunakan konstruktornya masing-masing.sumber