Saya mencoba menggunakan Fragmen dengan ViewPager
menggunakan FragmentPagerAdapter
. Yang ingin saya capai adalah mengganti sebuah fragmen, yang diposisikan di halaman pertama ViewPager
, dengan yang lain.
Pager terdiri dari dua halaman. Yang pertama adalah FirstPagerFragment
, yang kedua adalah SecondPagerFragment
. Mengklik tombol halaman pertama. Saya ingin mengganti FirstPagerFragment
dengan NextFragment.
Ada kode saya di bawah ini.
public class FragmentPagerActivity extends FragmentActivity {
static final int NUM_ITEMS = 2;
MyAdapter mAdapter;
ViewPager mPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_pager);
mAdapter = new MyAdapter(getSupportFragmentManager());
mPager = (ViewPager) findViewById(R.id.pager);
mPager.setAdapter(mAdapter);
}
/**
* Pager Adapter
*/
public static class MyAdapter extends FragmentPagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
@Override
public int getCount() {
return NUM_ITEMS;
}
@Override
public Fragment getItem(int position) {
if(position == 0) {
return FirstPageFragment.newInstance();
} else {
return SecondPageFragment.newInstance();
}
}
}
/**
* Second Page FRAGMENT
*/
public static class SecondPageFragment extends Fragment {
public static SecondPageFragment newInstance() {
SecondPageFragment f = new SecondPageFragment();
return f;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//Log.d("DEBUG", "onCreateView");
return inflater.inflate(R.layout.second, container, false);
}
}
/**
* FIRST PAGE FRAGMENT
*/
public static class FirstPageFragment extends Fragment {
Button button;
public static FirstPageFragment newInstance() {
FirstPageFragment f = new FirstPageFragment();
return f;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//Log.d("DEBUG", "onCreateView");
View root = inflater.inflate(R.layout.first, container, false);
button = (Button) root.findViewById(R.id.button);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
FragmentTransaction trans = getFragmentManager().beginTransaction();
trans.replace(R.id.first_fragment_root_id, NextFragment.newInstance());
trans.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
trans.addToBackStack(null);
trans.commit();
}
});
return root;
}
/**
* Next Page FRAGMENT in the First Page
*/
public static class NextFragment extends Fragment {
public static NextFragment newInstance() {
NextFragment f = new NextFragment();
return f;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//Log.d("DEBUG", "onCreateView");
return inflater.inflate(R.layout.next, container, false);
}
}
}
... dan di sini file xml
fragment_pager.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:padding="4dip"
android:gravity="center_horizontal"
android:layout_width="match_parent" android:layout_height="match_parent">
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1">
</android.support.v4.view.ViewPager>
</LinearLayout>
first.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/first_fragment_root_id"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button android:id="@+id/button"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="to next"/>
</LinearLayout>
Sekarang masalahnya ... ID mana yang harus saya gunakan
trans.replace(R.id.first_fragment_root_id, NextFragment.newInstance());
?
Jika saya gunakan R.id.first_fragment_root_id
, penggantian berfungsi, tetapi Hierarchy Viewer menunjukkan perilaku aneh, seperti di bawah ini.
Pada awalnya situasinya
setelah penggantian situasinya
Seperti yang Anda lihat ada sesuatu yang salah, saya berharap menemukan kondisi yang sama seperti pada gambar pertama setelah saya mengganti fragmen.
Jawaban:
Ada solusi lain yang tidak perlu memodifikasi kode sumber
ViewPager
danFragmentStatePagerAdapter
, dan bekerja denganFragmentPagerAdapter
kelas dasar yang digunakan oleh penulis.Saya ingin memulai dengan menjawab pertanyaan penulis tentang ID mana yang harus dia gunakan; itu adalah ID dari wadah, yaitu ID dari pager tampilan itu sendiri. Namun, seperti yang mungkin Anda perhatikan sendiri, menggunakan ID itu dalam kode Anda tidak menyebabkan apa-apa terjadi. Saya akan menjelaskan alasannya:
Pertama-tama, untuk membuat
ViewPager
penghuni kembali halaman, Anda perlu memanggilnotifyDataSetChanged()
yang berada di kelas dasar adaptor Anda.Kedua,
ViewPager
gunakangetItemPosition()
metode abstrak untuk memeriksa halaman mana yang harus dihancurkan dan mana yang harus disimpan. Implementasi default dari fungsi ini selalu kembaliPOSITION_UNCHANGED
, yang menyebabkanViewPager
untuk menjaga semua halaman saat ini, dan akibatnya tidak melampirkan halaman baru Anda. Jadi, untuk membuat penggantian fragmen berfungsi,getItemPosition()
perlu diganti dalam adaptor Anda dan harus kembaliPOSITION_NONE
ketika dipanggil dengan fragmen lama, untuk disembunyikan, sebagai argumen.Ini juga berarti bahwa adaptor Anda selalu perlu mengetahui bagian mana yang harus ditampilkan pada posisi 0,
FirstPageFragment
atauNextFragment
. Salah satu cara melakukan ini adalah memasok pendengar saat membuatFirstPageFragment
, yang akan dipanggil ketika saatnya untuk beralih fragmen. Saya pikir ini adalah hal yang baik, untuk membiarkan adaptor fragmen Anda menangani semua switch fragmen dan panggilan keViewPager
danFragmentManager
.Ketiga,
FragmentPagerAdapter
cache fragmen yang digunakan dengan nama yang berasal dari posisi, jadi jika ada fragmen di posisi 0, itu tidak akan diganti meskipun kelasnya baru. Ada dua solusi, tetapi yang paling sederhana adalah dengan menggunakanremove()
fungsiFragmentTransaction
, yang akan menghapus tag-nya juga.Itu banyak teks, di sini adalah kode yang harus bekerja dalam kasus Anda:
Semoga ini bisa membantu siapa saja!
sumber
FirstPageFragment.newInstance()
param pendengar?Pada 13 November 2012, mengganti fragmen dalam ViewPager tampaknya menjadi jauh lebih mudah. Google merilis Android 4.2 dengan dukungan untuk fragmen bersarang, dan itu juga didukung di Perpustakaan Dukungan Android v11 baru sehingga ini akan bekerja sepanjang jalan kembali ke 1,6
Ini sangat mirip dengan cara biasa mengganti sebuah fragmen kecuali Anda menggunakan getChildFragmentManager. Tampaknya berfungsi kecuali backstack fragmen bersarang tidak muncul ketika pengguna mengklik tombol kembali. Sesuai solusi dalam pertanyaan terkait itu, Anda perlu memanggil popBackStackImmediate () secara manual pada child manager dari fragmen. Jadi, Anda perlu mengganti onBackPressed () dari aktivitas ViewPager di mana Anda akan mendapatkan fragmen ViewPager saat ini dan memanggil getChildFragmentManager (). PopBackStackImmediate () di atasnya.
Mendapatkan fragmen yang saat ini sedang ditampilkan juga sedikit macet, saya menggunakan solusi "android: switcher yang kotor ini : VIEWPAGER_ID: INDEX" tetapi Anda juga dapat melacak sendiri semua fragmen ViewPager seperti yang dijelaskan dalam solusi kedua di halaman ini .
Jadi, inilah kode saya untuk ViewPager dengan 4 ListViews dengan tampilan detail yang ditampilkan di ViewPager ketika pengguna mengklik satu baris, dan dengan tombol kembali berfungsi. Saya mencoba memasukkan hanya kode yang relevan demi singkatnya, jadi tinggalkan komentar jika Anda ingin aplikasi lengkapnya diunggah ke GitHub.
HomeActivity.java
ListProductsFragment.java
sumber
Berdasarkan jawaban @wize, yang saya temukan bermanfaat dan elegan, saya dapat mencapai apa yang saya inginkan sebagian, karena saya ingin kemampuan untuk kembali ke Fragmen pertama setelah diganti. Saya mencapainya sedikit memodifikasi sedikit kodenya.
Ini akan menjadi FragmentPagerAdapter:
Untuk melakukan penggantian, cukup tentukan bidang statis, dari jenis
CalendarPageFragmentListener
dan diinisialisasi melaluinewInstance
metode fragmen yang sesuai dan panggilanFirstFragment.pageListener.onSwitchToNextFragment()
atau penuhNextFragment.pageListener.onSwitchToNextFragment()
respek.sumber
mFragmentAtPos0
referensi saat menyimpan status aktivitas. Ini bukan solusi yang paling elegan, tetapi berhasil.Saya telah menerapkan solusi untuk:
Trik untuk mencapai ini adalah sebagai berikut:
Kode adaptor adalah sebagai berikut:
Pertama kali Anda menambahkan semua tab, kita perlu memanggil metode createHistory (), untuk membuat sejarah awal
Setiap kali Anda ingin mengganti fragmen ke tab tertentu yang Anda panggil: replace (posisi int terakhir, Kelas fragmentClass akhir, args Bundel akhir)
Saat ditekan kembali, Anda perlu memanggil metode back ():
Solusinya bekerja dengan panel aksi sherlock dan dengan gerakan gesek.
sumber
tl; dr: Gunakan fragmen host yang bertanggung jawab untuk mengganti konten yang dihosting dan melacak riwayat navigasi belakang (seperti di browser).
Karena case use Anda terdiri dari jumlah tab yang tetap, solusi saya berfungsi dengan baik: Idenya adalah untuk mengisi ViewPager dengan instance dari kelas kustom
HostFragment
, yang dapat mengganti konten yang dihosting dan menyimpan sendiri riwayat navigasi. Untuk mengganti fragmen yang dihosting Anda membuat panggilan ke metodehostfragment.replaceFragment()
:Semua metode yang dilakukan adalah mengganti tata letak frame dengan id
R.id.hosted_fragment
dengan fragmen yang disediakan untuk metode.Lihat tutorial saya tentang topik ini untuk detail lebih lanjut dan contoh kerja lengkap tentang GitHub!
sumber
Beberapa solusi yang disajikan banyak membantu saya untuk menyelesaikan sebagian masalah tetapi masih ada satu hal penting yang hilang dalam solusi yang telah menghasilkan pengecualian yang tidak terduga dan konten halaman hitam daripada konten fragmen dalam beberapa kasus.
Masalahnya adalah bahwa kelas FragmentPagerAdapter menggunakan ID item untuk menyimpan fragmen yang di-cache ke FragmentManager . Untuk alasan ini, Anda perlu mengganti juga metode getItemId (posisi int) sehingga mengembalikan misalnya posisi untuk halaman tingkat atas dan posisi 100 + untuk halaman detail. Kalau tidak, fragmen tingkat atas yang sebelumnya dibuat akan dikembalikan dari cache alih-alih fragmen tingkat detail.
Selain itu, saya membagikan di sini contoh lengkap bagaimana menerapkan aktivitas seperti tab dengan halaman Fragmen menggunakan ViewPager dan tombol tab menggunakan RadioGroup yang memungkinkan penggantian halaman tingkat atas dengan halaman terperinci dan juga mendukung tombol kembali. Implementasi ini hanya mendukung satu tingkat susun belakang (daftar item - detail item) tetapi implementasi susun balik multi-level sangat mudah. Contoh ini bekerja cukup baik dalam kasus normal kecuali itu melemparkan NullPointerException jika Anda beralih ke misalnya halaman kedua, mengubah fragmen halaman pertama (sementara tidak terlihat) dan kembali ke halaman pertama. Saya akan mengirim solusi untuk masalah ini setelah saya mengetahuinya:
sumber
Saya telah membuat ViewPager dengan 3 elemen dan 2 sub elemen untuk indeks 2 dan 3 dan di sini apa yang ingin saya lakukan ..
Saya telah menerapkan ini dengan bantuan dari pertanyaan dan jawaban sebelumnya dari StackOverFlow dan di sini adalah tautannya.
LihatPagerChildFragments
sumber
Untuk mengganti fragmen di dalam
ViewPager
Anda dapat memindahkan kode sumberViewPager
,PagerAdapter
danFragmentStatePagerAdapter
kelas ke proyek Anda dan menambahkan kode berikut.ke dalam
ViewPager
:ke dalam FragmentStatePagerAdapter:
handleGetItemInvalidated()
memastikan bahwa setelah panggilan berikutnyagetItem()
mengembalikan newFragmentgetFragmentPosition()
mengembalikan posisi fragmen di adaptor Anda.Sekarang, untuk mengganti panggilan fragmen
Jika Anda tertarik pada contoh proyek, tanyakan sumbernya kepada saya.
sumber
Bekerja sangat baik dengan solusi AndroidTeam, namun saya menemukan bahwa saya membutuhkan kemampuan untuk kembali seperti halnya
FrgmentTransaction.addToBackStack(null)
Tetapi menambahkan ini hanya akan menyebabkan Fragmen diganti tanpa memberi tahu ViewPager. Menggabungkan solusi yang disediakan dengan peningkatan kecil ini akan memungkinkan Anda untuk kembali ke keadaan sebelumnya hanya dengan mengesampingkan metode aktivitasonBackPressed()
. Kerugian terbesar adalah bahwa ia hanya akan kembali satu per satu yang dapat menghasilkan banyak klik kembaliSemoga ini bisa membantu seseorang.
Juga sejauh
getFragmentPosition()
ini cukup banyakgetItem()
terbalik. Anda tahu fragmen mana yang digunakan, pastikan Anda mengembalikan posisi yang benar. Berikut ini contohnya:sumber
Dalam
onCreateView
metode Anda ,container
sebenarnya aViewPager
contoh.Jadi, hanya menelepon
akan mengubah fragmen saat ini di
ViewPager
.sumber
vpViewPager.setCurrentItem(1);
. Saya membaca contoh setiap orang, dengan tidak ada yang terjadi setiap kali sampai saya akhirnya mendapatkan milik Anda. Terima kasih.ViewPager
ke halaman lain, itu tidak akan menggantikan bagian apa pun.Inilah solusi saya yang relatif sederhana untuk masalah ini. Kunci dari solusi ini adalah untuk digunakan
FragmentStatePagerAdapter
alih-alihFragmentPagerAdapter
karena yang pertama akan menghapus fragmen yang tidak digunakan untuk Anda sementara yang kemudian masih mempertahankan instans mereka. Yang kedua adalah penggunaanPOSITION_NONE
in getItem (). Saya telah menggunakan Daftar sederhana untuk melacak fragmen saya. Persyaratan saya adalah mengganti seluruh daftar fragmen sekaligus dengan daftar baru, tetapi di bawah ini dapat dengan mudah dimodifikasi untuk mengganti masing-masing fragmen:sumber
Saya juga membuat solusi, yang bekerja dengan Stacks . Ini adalah pendekatan yang lebih modular sehingga Anda tidak perlu menentukan masing-masing Fragmen dan Fragmen Detail di Anda
FragmentPagerAdapter
. Itu dibangun di atas Contoh dari ActionbarSherlock yang berasal jika saya langsung dari Aplikasi Demo Google.Tambahkan ini untuk fungsi tombol kembali di MainActivity Anda:
Jika Anda ingin menyimpan Status Fragmen ketika dihapus. Biarkan Fragmen Anda menerapkan antarmuka
SaveStateBundle
kembali dalam fungsi bundel dengan status penyimpanan Anda. Dapatkan bundel setelah instantiasi olehthis.getArguments()
.Anda dapat instantiate tab seperti ini:
berfungsi serupa jika Anda ingin menambahkan Fragmen di atas Tab Stack. Penting : Saya pikir, itu tidak akan berfungsi jika Anda ingin memiliki 2 instance dari kelas yang sama di atas dua Tab. Saya melakukan solusi ini bersama-sama dengan cepat, jadi saya hanya bisa membagikannya tanpa memberikan pengalaman apa pun dengannya.
sumber
Mengganti fragmen dalam viewpager cukup terlibat tetapi sangat mungkin dan bisa terlihat sangat apik. Pertama, Anda harus membiarkan viewpager itu sendiri menangani penghapusan dan penambahan fragmen. Apa yang terjadi adalah ketika Anda mengganti fragmen di dalam SearchFragment, viewpager Anda mempertahankan tampilan fragmennya. Jadi Anda berakhir dengan halaman kosong karena SearchFragment dihapus ketika Anda mencoba untuk menggantinya.
Solusinya adalah membuat pendengar di dalam viewpager Anda yang akan menangani perubahan yang dibuat di luarnya jadi pertama-tama tambahkan kode ini ke bagian bawah adaptor Anda.
Maka Anda perlu membuat kelas pribadi di viewpager Anda yang menjadi pendengar ketika Anda ingin mengubah fragmen Anda. Misalnya Anda bisa menambahkan sesuatu seperti ini. Perhatikan bahwa itu mengimplementasikan antarmuka yang baru saja dibuat. Jadi, setiap kali Anda memanggil metode ini, itu akan menjalankan kode di dalam kelas di bawah ini.
Ada dua hal utama untuk ditunjukkan di sini:
Perhatikan pendengar yang ditempatkan di konstruktor 'newInstance (listener). Ini adalah bagaimana Anda akan memanggilfragment0Changed (String newFragmentIdentification) `. Kode berikut menunjukkan bagaimana Anda membuat pendengar di dalam fragmen Anda.
static nextFragmentListener listenerSearch;
Anda dapat memanggil perubahan di bagian dalam
onPostExecute
Ini akan memicu kode di dalam viewpager Anda untuk mengganti fragmen Anda di posisi nol fragAt0 menjadi searchResultFragment baru. Ada dua potong kecil yang perlu Anda tambahkan ke viewpager sebelum menjadi fungsional.
Satu akan berada di metode getItem menimpa viewpager.
Sekarang tanpa bagian terakhir ini Anda masih akan mendapatkan halaman kosong. Agak timpang, tetapi ini adalah bagian penting dari viewPager. Anda harus mengganti metode getItemPosition pada viewpager. Biasanya metode ini akan mengembalikan POSITION_UNCHANGED yang memberi tahu viewpager agar semuanya tetap sama sehingga getItem tidak akan pernah dipanggil untuk menempatkan fragmen baru di halaman. Inilah contoh sesuatu yang bisa Anda lakukan
Seperti yang saya katakan, kode menjadi sangat terlibat, tetapi pada dasarnya Anda harus membuat adaptor khusus untuk situasi Anda. Hal-hal yang saya sebutkan akan memungkinkan untuk mengubah fragmen. Mungkin akan membutuhkan waktu lama untuk merendam semuanya sehingga saya akan bersabar, tetapi semuanya akan masuk akal. Ini benar-benar layak diluangkan waktu karena dapat membuat aplikasi yang tampak sangat apik.
Inilah nugget untuk menangani tombol kembali. Anda memasukkan ini ke dalam MainActivity Anda
Anda perlu membuat metode yang disebut backPressed () di dalam FragmentSearchResult yang memanggil fragment0 berubah. Ini bersamaan dengan kode yang saya tunjukkan sebelumnya akan menangani menekan tombol kembali. Semoga berhasil dengan kode Anda untuk mengubah viewpager. Butuh banyak pekerjaan, dan sejauh yang saya temukan, tidak ada adaptasi cepat. Seperti yang saya katakan, pada dasarnya Anda membuat adaptor viewpager khusus, dan membiarkannya menangani semua perubahan yang diperlukan menggunakan pendengar
sumber
Ini cara saya untuk mencapai itu.
Pertama-tama tambahkan
Root_fragment
viewPager
tab di dalam di mana Anda ingin menerapkanfragment
acara klik tombol . Contoh;Pertama-tama,
RootTabFragment
harus dimasukkanFragmentLayout
untuk perubahan fragmen.Lalu, di dalam
RootTabFragment
onCreateView
, terapkanfragmentChange
untuk AndaFirstPagerFragment
Setelah itu, implementasikan
onClick
acara untuk tombol Anda di dalamFirstPagerFragment
dan buat perubahan fragmen seperti itu lagi.Semoga ini bisa membantu kalian.
sumber
Saya menemukan solusi sederhana, yang berfungsi dengan baik bahkan jika Anda ingin menambahkan fragmen baru di tengah atau mengganti fragmen saat ini. Dalam solusi saya, Anda harus menimpa
getItemId()
yang harus mengembalikan id unik untuk setiap fragmen. Bukan posisi sebagai default.Itu dia:
Perhatikan: Dalam contoh ini
FirstFragment
danSecondFragment
perpanjang kelas PageFragment abstrak, yang memiliki metodegetPage()
.sumber
Saya melakukan sesuatu yang mirip dengan wize tetapi dalam jawaban saya Anda dapat mengubah antara dua fragmen kapan pun Anda inginkan. Dan dengan jawaban bijak saya punya beberapa masalah ketika mengubah orientasi layar dan hal-hal seperti itu. Ini adalah PagerAdapter seperti:
Pendengar yang saya terapkan dalam aktivitas wadah adaptor untuk memasukkannya ke fragmen ketika melampirkannya, ini adalah aktivitasnya:
Kemudian dalam fragmen menempatkan pendengar ketika melampirkan memanggilnya:
Dan akhirnya pendengar:
sumber
Saya mengikuti jawaban oleh @wize dan @mdelolmo dan saya mendapatkan solusinya. Terima kasih banyak. Tapi, saya menyetel solusi ini sedikit untuk meningkatkan konsumsi memori.
Masalah yang saya amati:
Mereka menyimpan contoh
Fragment
yang diganti. Dalam kasus saya, itu adalah Fragmen yang memegangMapView
dan saya pikir itu mahal. Jadi, saya menjagaFragmentPagerPositionChanged (POSITION_NONE or POSITION_UNCHANGED)
bukannyaFragment
itu sendiri.Ini implementasi saya.
Tautan demo di sini .. https://youtu.be/l_62uhKkLyM
Untuk keperluan demo, gunakan 2 fragmen
TabReplaceFragment
danDemoTab2Fragment
di posisi dua. Dalam semua kasus lain saya menggunakanDemoTabFragment
contoh.Penjelasan:
Saya beralih
Switch
dari Aktivitas keDemoCollectionPagerAdapter
. Berdasarkan keadaan sakelar ini, kami akan menampilkan fragmen yang benar. Ketika cek saklar berubah, saya akan meneleponSwitchFragListener
'sonSwitchToNextFragment
metode, di mana saya mengubah nilaipagerAdapterPosChanged
variabelPOSITION_NONE
. Lihat lebih lanjut tentang POSITION_NONE . Ini akan membatalkan getItem dan saya memiliki logika untuk membuat fragmen yang tepat di sana. Maaf, jika penjelasannya agak berantakan.Sekali lagi terima kasih banyak kepada @wize dan @mdelolmo untuk ide aslinya.
Semoga ini bisa membantu. :)
Beritahu saya jika implementasi ini memiliki kekurangan. Itu akan sangat membantu proyek saya.
sumber
setelah penelitian saya menemukan solusi dengan kode pendek. pertama-tama buat instance publik tentang fragmen dan hapus saja fragmen Anda di onSaveInstanceState jika fragmen tidak dibuat ulang saat perubahan orientasi.
sumber