Saya mencoba menambahkan dan menghapus Fragmen dari ViewPager secara dinamis, menambahkan karya tanpa masalah, tetapi menghapus tidak berfungsi seperti yang diharapkan.
Setiap kali saya ingin menghapus item saat ini, yang terakhir dihapus.
Saya juga mencoba menggunakan FragmentStatePagerAdapter atau mengembalikan POSITION_NONE dalam metode getItemPosition adaptor.
Apa yang saya lakukan salah?
Inilah contoh dasar:
MainActivity.java
public class MainActivity extends FragmentActivity implements TextProvider {
private Button mAdd;
private Button mRemove;
private ViewPager mPager;
private MyPagerAdapter mAdapter;
private ArrayList<String> mEntries = new ArrayList<String>();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mEntries.add("pos 1");
mEntries.add("pos 2");
mEntries.add("pos 3");
mEntries.add("pos 4");
mEntries.add("pos 5");
mAdd = (Button) findViewById(R.id.add);
mRemove = (Button) findViewById(R.id.remove);
mPager = (ViewPager) findViewById(R.id.pager);
mAdd.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
addNewItem();
}
});
mRemove.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
removeCurrentItem();
}
});
mAdapter = new MyPagerAdapter(this.getSupportFragmentManager(), this);
mPager.setAdapter(mAdapter);
}
private void addNewItem() {
mEntries.add("new item");
mAdapter.notifyDataSetChanged();
}
private void removeCurrentItem() {
int position = mPager.getCurrentItem();
mEntries.remove(position);
mAdapter.notifyDataSetChanged();
}
@Override
public String getTextForPosition(int position) {
return mEntries.get(position);
}
@Override
public int getCount() {
return mEntries.size();
}
private class MyPagerAdapter extends FragmentPagerAdapter {
private TextProvider mProvider;
public MyPagerAdapter(FragmentManager fm, TextProvider provider) {
super(fm);
this.mProvider = provider;
}
@Override
public Fragment getItem(int position) {
return MyFragment.newInstance(mProvider.getTextForPosition(position));
}
@Override
public int getCount() {
return mProvider.getCount();
}
}
}
TextProvider.java
public interface TextProvider {
public String getTextForPosition(int position);
public int getCount();
}
MyFragment.java
public class MyFragment extends Fragment {
private String mText;
public static MyFragment newInstance(String text) {
MyFragment f = new MyFragment(text);
return f;
}
public MyFragment() {
}
public MyFragment(String text) {
this.mText = text;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment, container, false);
((TextView) root.findViewById(R.id.position)).setText(mText);
return root;
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="@+id/add"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="add new item" />
<Button
android:id="@+id/remove"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="remove current item" />
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1" />
</LinearLayout>
fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/position"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="35sp" />
</LinearLayout>
MyFragment.java
??Jawaban:
Solusi oleh Louth tidak cukup untuk membuat semuanya bekerja untuk saya, karena fragmen yang ada tidak dihancurkan. Termotivasi oleh jawaban ini , saya menemukan bahwa solusinya adalah mengesampingkan
getItemId(int position)
metodeFragmentPagerAdapter
untuk memberikan ID unik baru setiap kali ada perubahan dalam posisi yang diharapkan dari sebuah Fragmen.Kode sumber:
Sekarang, misalnya jika Anda menghapus satu tab atau mengubah pesanan, Anda harus menelepon
notifyChangeInPosition(1)
sebelum meneleponnotifyDataSetChanged()
, yang akan memastikan bahwa semua Fragmen akan dibuat kembali.Mengapa solusi ini berhasil?
Mengganti getItemPosition ():
Saat
notifyDataSetChanged()
dipanggil, adaptor memanggilnotifyChanged()
metodeViewPager
yang dilampirkan. TheViewPager
cek maka nilai yang dikembalikan oleh adaptorgetItemPosition()
untuk setiap item, menghapus item-item yang kembaliPOSITION_NONE
(lihat kode sumber ) dan kemudian repopulating.Mengganti getItemId ():
Ini diperlukan untuk mencegah adaptor memuat ulang fragmen lama saat
ViewPager
dihuni kembali. Anda dapat dengan mudah memahami mengapa ini bekerja dengan melihat kode sumber untuk instantiateItem () diFragmentPagerAdapter
.Seperti yang Anda lihat,
getItem()
metode ini hanya dipanggil jika manajer fragmen tidak menemukan fragmen yang ada dengan Id yang sama. Bagi saya sepertinya ada bug yang masih menempel pada fragmen lama setelahnotifyDataSetChanged()
dipanggil, tetapi dokumentasi untuknyaViewPager
dengan jelas menyatakan bahwa:Jadi semoga solusi yang diberikan di sini tidak diperlukan dalam versi perpustakaan dukungan yang akan datang.
sumber
notifyDataSetChanged()
bekerja.ViewPager tidak menghapus fragmen Anda dengan kode di atas karena memuat beberapa tampilan (atau fragmen dalam kasus Anda) ke dalam memori. Selain tampilan yang terlihat, itu juga memuat tampilan ke kedua sisi yang terlihat. Ini memberikan pengguliran yang mulus dari tampilan ke tampilan yang membuat ViewPager sangat keren.
Untuk mencapai efek yang Anda inginkan, Anda perlu melakukan beberapa hal.
Ubah FragmentPagerAdapter ke FragmentStatePagerAdapter. Alasan untuk ini adalah bahwa FragmentPagerAdapter akan menyimpan semua pandangan yang dimuat ke dalam memori selamanya. Di mana FragmentStatePagerAdapter membuang tampilan yang berada di luar tampilan saat ini dan yang dapat dilalui.
Ganti metode adapter getItemPosition (ditampilkan di bawah). Ketika kami memanggil
mAdapter.notifyDataSetChanged();
ViewPager menginterogasi adaptor untuk menentukan apa yang telah berubah dalam hal penentuan posisi. Kami menggunakan metode ini untuk mengatakan bahwa semuanya telah berubah sehingga memproses ulang semua pemosisian tampilan Anda.Dan ini kodenya ...
sumber
solusi kerja saya untuk menghapus halaman fragmen dari pager tampilan
Dan ketika saya perlu menghapus beberapa halaman dengan indeks saya melakukan ini
Ini dia inisialisasi adaptor saya
sumber
Fragmen harus sudah dihapus tetapi masalahnya adalah viewpager save state
Mencoba
Tidak ada yang berhasil tetapi ini menyelesaikan masalah!
Bersulang !
sumber
Saya punya ide hanya menyalin kode sumber dari
android.support.v4.app.FragmentPagerAdpater
ke kelas kustom bernamaCustumFragmentPagerAdapter
. Ini memberi saya kesempatan untuk memodifikasiinstantiateItem(...)
sehingga setiap kali dipanggil, ia menghapus / menghancurkan fragmen yang terpasang saat ini sebelum menambahkan fragmen baru yang diterima darigetItem()
metode.Cukup modifikasi dengan
instantiateItem(...)
cara berikut:sumber
Anda dapat menggabungkan keduanya menjadi lebih baik:
sumber
Untuk pembaca masa depan!
Sekarang Anda dapat menggunakan ViewPager2 untuk menambahkan secara dinamis, menghapus fragmen dari viewpager.
Mengutip referensi bentuk API
Lihatlah
MutableCollectionFragmentActivity.kt
di googlesample / android-viewpager2 untuk contoh menambahkan, menghapus fragmen secara dinamis dari viewpager.Untuk informasi anda:
Artikel:
Referensi API
Catatan rilis
Sampel Repo: https://github.com/googlesamples/android-viewpager2
sumber
Jawaban Louth bekerja dengan baik. Tapi saya tidak berpikir selalu mengembalikan POSITION_NONE adalah ide yang bagus. Karena POSITION_NONE berarti bahwa fragmen harus dihancurkan dan fragmen baru akan dibuat. Anda dapat memeriksa bahwa dalam fungsi dataSetChanged dalam kode sumber ViewPager.
Jadi saya pikir Anda sebaiknya menggunakan arraylist dariReferensi lemah untuk menyimpan semua fragmen yang telah Anda buat. Dan ketika Anda menambah atau menghapus beberapa halaman, Anda bisa mendapatkan posisi yang tepat dari daftar array Anda sendiri.
Menurut komentar, getItemPosition dipanggil ketika tampilan host berusaha untuk menentukan apakah posisi item telah berubah. Dan nilai pengembalian berarti posisi baru.
Tapi ini tidak cukup. Kami masih memiliki langkah penting untuk diambil. Dalam kode sumber dari FragmentStatePagerAdapter, ada array bernama "mFragments" cache fragmen yang tidak dihancurkan. Dan dalam fungsi instantiateItem.
Itu mengembalikan fragmen cache langsung ketika menemukan bahwa fragmen cache tidak nol. Jadi ada masalah. Dari contoh, mari kita hapus satu halaman di posisi 2, Pertama, Kami menghapus fragmen itu dari daftar array referensi kami sendiri. jadi di getItemPosition, ia akan mengembalikan POSITION_NONE untuk fragmen itu, dan kemudian fragmen itu akan dihancurkan dan dihapus dari "mFragments".
Sekarang fragmen di posisi 3 akan berada di posisi 2. Dan instantiatedItem dengan posisi param 3 akan dipanggil. Pada saat ini, item ketiga dalam "mFramgents" bukan null, sehingga akan langsung dikembalikan. Tapi sebenarnya yang dikembalikan adalah fragmen di posisi 2. Jadi ketika kita beralih ke halaman 3, kita akan menemukan halaman kosong di sana.
Untuk mengatasi masalah ini. Saran saya adalah Anda dapat menyalin kode sumber FragmentStatePagerAdapter ke proyek Anda sendiri, dan ketika Anda menambah atau menghapus operasi, Anda harus menambah dan menghapus elemen dalam daftar array "mFragments".
Hal-hal akan lebih mudah jika Anda hanya menggunakan PagerAdapter daripada FragmentStatePagerAdapter. Semoga berhasil.
sumber
sumber
Saya punya beberapa masalah dengan
FragmentStatePagerAdapter
. Setelah menghapus item:Setelah banyak percobaan, saya datang dengan solusi berikut.
Solusi ini hanya menggunakan peretasan jika akan menghapus item. Kalau tidak (misalnya saat menambahkan item) itu mempertahankan kebersihan dan kinerja kode asli.
Tentu saja, dari luar adaptor, Anda hanya memanggil addItem / removeItem, tidak perlu memanggil notifyDataSetChanged ().
sumber
Coba solusi ini. Saya telah menggunakan penyatuan data untuk tampilan yang mengikat. Anda dapat menggunakan fungsi "findViewById ()" yang umum.
sumber
Saya menambahkan fungsi "clearFragments" dan saya menggunakan fungsi itu untuk menghapus adaptor sebelum mengatur fragmen baru. Ini menyebut tindakan penghapusan yang tepat dari Fragmen. Kelas pagerAdapter saya:
sumber
saya memecahkan masalah ini dengan langkah-langkah ini
1- gunakan FragmentPagerAdapter
2- di setiap fragmen buat id acak
3- mengganti getItemPosition di adaptor
4-override getItemId di adaptor
5- sekarang hapus kode
ini bekerja untuk saya, saya harap membantu
sumber
Kode versi terakhir saya, memperbaiki SEMUA bug. Saya butuh 3 hari
Diperbarui 2020/07/18: Saya telah mengubah banyak kode sumber dan memperbaiki begitu banyak bug, tapi saya tidak berjanji itu masih berfungsi hari ini.
https://github.com/lin1987www/FragmentBuilder/blob/master/commonLibrary/src/main/java/android/support/v4/app/FragmentStatePagerAdapterFix.java
sumber
Anda bisa mengganti
destroyItem
metode tersebutsumber
Saya harap ini dapat membantu apa yang Anda inginkan.
sumber