Hapus Halaman Fragmen dari ViewPager di Android

138

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>
astuetz
sumber
2
+1 untuk kode sumber
wizurd
apakah benar menggunakan konstruktor non-default di MyFragment.java??
codeKiller

Jawaban:

95

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)metode FragmentPagerAdapteruntuk memberikan ID unik baru setiap kali ada perubahan dalam posisi yang diharapkan dari sebuah Fragmen.

Kode sumber:

private class MyPagerAdapter extends FragmentPagerAdapter {

    private TextProvider mProvider;
    private long baseId = 0;

    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();
    }


    //this is called when notifyDataSetChanged() is called
    @Override
    public int getItemPosition(Object object) {
        // refresh all fragments when data set changed
        return PagerAdapter.POSITION_NONE;
    }


    @Override
    public long getItemId(int position) {
        // give an ID different from position when position has been changed
        return baseId + position;
    }

    /**
     * Notify that the position of a fragment has been changed.
     * Create a new ID for each position to force recreation of the fragment
     * @param n number of items which have been changed
     */
    public void notifyChangeInPosition(int n) {
        // shift the ID returned by getItemId outside the range of all previous fragments
        baseId += getCount() + n;
    }
}

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 memanggil notifyChanged()metode ViewPageryang dilampirkan. The ViewPagercek maka nilai yang dikembalikan oleh adaptor getItemPosition()untuk setiap item, menghapus item-item yang kembali POSITION_NONE(lihat kode sumber ) dan kemudian repopulating.

Mengganti getItemId ():

Ini diperlukan untuk mencegah adaptor memuat ulang fragmen lama saat ViewPagerdihuni kembali. Anda dapat dengan mudah memahami mengapa ini bekerja dengan melihat kode sumber untuk instantiateItem () di FragmentPagerAdapter.

    final long itemId = getItemId(position);

    // Do we already have this fragment?
    String name = makeFragmentName(container.getId(), itemId);
    Fragment fragment = mFragmentManager.findFragmentByTag(name);
    if (fragment != null) {
        if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
        mCurTransaction.attach(fragment);
    } else {
        fragment = getItem(position);
        if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
        mCurTransaction.add(container.getId(), fragment,
                makeFragmentName(container.getId(), itemId));
    }

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 setelah notifyDataSetChanged()dipanggil, tetapi dokumentasi untuknya ViewPagerdengan jelas menyatakan bahwa:

Catatan kelas ini saat ini sedang dalam desain dan pengembangan awal. API kemungkinan akan berubah di pemutakhiran selanjutnya dari perpustakaan kompatibilitas, yang memerlukan perubahan pada kode sumber aplikasi ketika mereka dikompilasi dengan versi yang lebih baru.

Jadi semoga solusi yang diberikan di sini tidak diperlukan dalam versi perpustakaan dukungan yang akan datang.

Tim Rae
sumber
1
Terima kasih banyak atas jawaban ini! Sangat melelahkan untuk mencoba memecahkan masalah ini tanpa menggunakan FragmentStatePagerAdapter. Penjelasan yang bagus juga. Sudahkah Anda menemukan cara untuk mendapatkan getItem untuk dipicu ketika kembali ke fragmen? Saya sudah mencoba menggunakan notifyDataSetChanged () pintar Anda di berbagai tempat (mis. OnStop) tanpa hasil
u2tall
Saya benar-benar tidak mengerti pertanyaan Anda ... Silakan posting pertanyaan baru termasuk kode sumber jika Anda tidak dapat menemukannya.
Tim Rae
1
2016, solusi ini masih diperlukan untuk membuatnya notifyDataSetChanged()bekerja.
Subin Sebastian
Wow! Penjelasan luar biasa. Saya memiliki viewpager yang cukup kompleks di mana saya menghapus dan memasukkan item secara dinamis dan tidak akan membuatnya bekerja tanpa memahami semua ini.
pecah
Masih membutuhkan solusi ini untuk mengganti fragmen lama. Sial.
kopikaokao
291

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.

  1. 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.

  2. 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 ...

private class MyPagerAdapter extends FragmentStatePagerAdapter {

    //... your existing code

    @Override
    public int getItemPosition(Object object){
        return PagerAdapter.POSITION_NONE;
    }

}
Mulut
sumber
1
Oke, ini sepertinya solusi termudah untuk masalah ini. Meskipun beberapa solusi lain dibahas pada laporan bug di sini: code.google.com/p/android/issues/detail?id=19110 dan di sini: code.google.com/p/android/issues/detail?id= 19001
Subin Sebastian
@Louth: Saya menggunakan ViewPager dengan TabsAdapter, seperti yang diperluas dari FragmentStatePagerAdapter (per contoh di halaman ref Google: developer.android.com/reference/android/support/v4/view/… ). Pertanyaan tindak lanjut saya adalah: bagaimana Anda menghapus tab di tempat pertama? Terima kasih.
gcl1
4
@Louth Ketika kita memanggil notifyDataSetChanged () viewpager membuat fragmen baru, tetapi yang lama masih ada dalam memori. Dan masalah saya adalah; mereka menerima panggilan untuk acara onOptionsItemSelected mereka. Bagaimana saya bisa menyingkirkan fragmen lama?
syloc
1
Terima kasih, hanya mengganti FragmentPagerAdapter ke FragmentStatePagerAdapter menyelesaikan masalah saya. Setelah itu saya hanya menghapus AllViews () di ViewPager dan memuat kembali fragmen secara dinamis.
Michal
17
Mengapa Anda selalu mengembalikan POSITION_NONE daripada benar-benar melihat jika tampilan / fragmen / objek yang diberikan masih ada dan valid? Metode ini seharusnya menjawab pertanyaan "hei, bisakah saya menggunakan kembali yang ini?" - dan selalu berkata "Tidak!" hanya berarti rekreasi segalanya! Ini tidak perlu dalam banyak kasus.
Zordid
14

solusi kerja saya untuk menghapus halaman fragmen dari pager tampilan

public class MyFragmentAdapter extends FragmentStatePagerAdapter {

    private ArrayList<ItemFragment> pages;

    public MyFragmentAdapter(FragmentManager fragmentManager, ArrayList<ItemFragment> pages) {
        super(fragmentManager);
        this.pages = pages;
    }

    @Override
    public Fragment getItem(int index) {
        return pages.get(index);
    }

    @Override
    public int getCount() {
        return pages.size();
    }

    @Override
    public int getItemPosition(Object object) {
        int index = pages.indexOf (object);

        if (index == -1)
            return POSITION_NONE;
        else
            return index;
    }
}

Dan ketika saya perlu menghapus beberapa halaman dengan indeks saya melakukan ini

pages.remove(position); // ArrayList<ItemFragment>
adapter.notifyDataSetChanged(); // MyFragmentAdapter

Ini dia inisialisasi adaptor saya

MyFragmentAdapter adapter = new MyFragmentAdapter(getSupportFragmentManager(), pages);
viewPager.setAdapter(adapter);
Vasil Valchev
sumber
Bisakah Anda berbagi kode untuk penambahan fragmen?
VVB
Tetapi sementara menambahkan itu mencerminkan lambat setelah menggesek dua kali / tiga kali
VVB
jika Anda tidak menghapus item, itu hanya mengembalikan item, tidak lebih ... apakah Anda menguji pada vm atau perangkat nyata?
Vasil Valchev
Coba solusi lain, jawab sedikit lebih banyak suara. Lihat pager memiliki sistem pencairan yang sangat kompleks untuk kinerja yang lebih baik di swip, jadi peretasan ini tidak ditangani oleh pager tampilan asli dengan tujuan ...
Vasil Valchev
11

Fragmen harus sudah dihapus tetapi masalahnya adalah viewpager save state

Mencoba

myViewPager.setSaveFromParentEnabled(false);

Tidak ada yang berhasil tetapi ini menyelesaikan masalah!

Bersulang !

Kathan Shah
sumber
2

Saya punya ide hanya menyalin kode sumber dari android.support.v4.app.FragmentPagerAdpaterke kelas kustom bernama CustumFragmentPagerAdapter. Ini memberi saya kesempatan untuk memodifikasi instantiateItem(...)sehingga setiap kali dipanggil, ia menghapus / menghancurkan fragmen yang terpasang saat ini sebelum menambahkan fragmen baru yang diterima dari getItem()metode.

Cukup modifikasi dengan instantiateItem(...)cara berikut:

@Override
public Object instantiateItem(ViewGroup container, int position) {
    if (mCurTransaction == null) {
        mCurTransaction = mFragmentManager.beginTransaction();
    }
    final long itemId = getItemId(position);

    // Do we already have this fragment?
    String name = makeFragmentName(container.getId(), itemId);
    Fragment fragment = mFragmentManager.findFragmentByTag(name);

    // remove / destroy current fragment
    if (fragment != null) {
        mCurTransaction.remove(fragment);
    }

    // get new fragment and add it
    fragment = getItem(position);
    mCurTransaction.add(container.getId(), fragment,    makeFragmentName(container.getId(), itemId));

    if (fragment != mCurrentPrimaryItem) {
        fragment.setMenuVisibility(false);
        fragment.setUserVisibleHint(false);
    }

    return fragment;
}
Sven
sumber
1

Anda dapat menggabungkan keduanya menjadi lebih baik:

private class MyPagerAdapter extends FragmentStatePagerAdapter {

    //... your existing code

    @Override
    public int getItemPosition(Object object){

      if(Any_Reason_You_WantTo_Update_Positions) //this includes deleting or adding pages
 return PagerAdapter.POSITION_NONE;
    }
else
return PagerAdapter.POSITION_UNCHANGED; //this ensures high performance in other operations such as editing list items.

}
MSaudi
sumber
Ini hanya berfungsi jika Anda menghapus tab paling kanan, untuk menghapus tab dalam atau tab kiri, Anda harus menyusun ulang tab dan mengembalikan posisi baru seperti yang dinyatakan dalam pengembang
BitByteDog
1

Untuk pembaca masa depan!

Sekarang Anda dapat menggunakan ViewPager2 untuk menambahkan secara dinamis, menghapus fragmen dari viewpager.

Mengutip referensi bentuk API

ViewPager2 menggantikan ViewPager, menangani sebagian besar titik nyeri pendahulunya, termasuk dukungan tata letak kanan-ke-kiri, orientasi vertikal, koleksi Fragmen yang dapat dimodifikasi, dll.

Lihatlah MutableCollectionFragmentActivity.ktdi 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

pengguna158
sumber
Saya memiliki 2 fragmen mainfragment dan fragmen lainnya. Saya menambahkanfragment lain setelah setiap posisi ke-5 dan tidak ada dalam daftar array jadi bagaimana cara menghapusfragment lain itu jika tidak ada dalam daftar? Saya menggunakan viewpager2
b devloper
0

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.

        if (newPos == PagerAdapter.POSITION_NONE) {
            mItems.remove(i);
            i--;
            ... not related code
            mAdapter.destroyItem(this, ii.position, ii.object);

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.

 public int getItemPosition(Object object) {
    for (int i = 0; i < mFragmentsReferences.size(); i ++) {
        WeakReference<Fragment> reference = mFragmentsReferences.get(i);
        if (reference != null && reference.get() != null) {
            Fragment fragment = reference.get();
            if (fragment == object) {
                return i;
            }
        }
    }
    return POSITION_NONE;
}

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.

if (mFragments.size() > position) {
        Fragment f = mFragments.get(position);
        if (f != null) {
            return f;
        }
    }

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".

 mFragments.set(position, null);

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.

orang yang lewat
sumber
0

menambah atau menghapus fragmen dalam viewpager secara dinamis.
Panggil setupViewPager (viewPager) saat aktivitas dimulai. Untuk memuat setup fragmen panggilan yang berbeda, ViewPagerCustom (viewPager). mis. panggilan klik tombol: setupViewPagerCustom (viewPager);

    private void setupViewPager(ViewPager viewPager)
{
    ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
    adapter.addFrag(new fragmnet1(), "HOME");
    adapter.addFrag(new fragmnet2(), "SERVICES");

    viewPager.setAdapter(adapter);
}

private void setupViewPagerCustom(ViewPager viewPager)
{

    ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());

    adapter.addFrag(new fragmnet3(), "Contact us");
    adapter.addFrag(new fragmnet4(), "ABOUT US");


    viewPager.setAdapter(adapter);
}

//Viewpageradapter, handles the views

static class ViewPagerAdapter extends FragmentStatePagerAdapter
{
    private final List<Fragment> mFragmentList = new ArrayList<>();
    private final List<String> mFragmentTitleList = new ArrayList<>();

    public ViewPagerAdapter(FragmentManager manager){
        super(manager);
    }

    @Override
    public Fragment getItem(int position) {
        return mFragmentList.get(position);
    }

    @Override
    public int getCount() {
        return mFragmentList.size();
    }

    @Override
    public int getItemPosition(Object object){
        return PagerAdapter.POSITION_NONE;
    }

    public void addFrag(Fragment fragment, String title){
        mFragmentList.add(fragment);
        mFragmentTitleList.add(title);
    }

    @Override
    public CharSequence getPageTitle(int position){
        return mFragmentTitleList.get(position);
    }
}
Riad Hasan
sumber
0

Saya punya beberapa masalah dengan FragmentStatePagerAdapter. Setelah menghapus item:

  • ada item lain yang digunakan untuk suatu posisi (item yang bukan milik posisi tetapi untuk posisi di sebelahnya)
  • atau beberapa fragmen tidak dimuat (hanya ada latar belakang kosong yang terlihat pada halaman itu)

Setelah banyak percobaan, saya datang dengan solusi berikut.

public class SomeAdapter extends FragmentStatePagerAdapter {

    private List<Item> items = new ArrayList<Item>();

    private boolean removing;

    @Override
    public Fragment getItem(int position) {
        ItemFragment fragment = new ItemFragment();
        Bundle args = new Bundle();
        // use items.get(position) to configure fragment
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public int getCount() {
        return items.size();
    }

    @Override
    public int getItemPosition(Object object) {
        if (removing) {
            return PagerAdapter.POSITION_NONE;
        }

        Item item = getItemOfFragment(object);

        int index = items.indexOf(item);
        if (index == -1) {
            return POSITION_NONE;
        } else {
            return index;
        }
    }

   public void addItem(Item item) {
        items.add(item);
        notifyDataSetChanged();
    }

    public void removeItem(int position) {
        items.remove(position);

        removing = true;
        notifyDataSetChanged();
        removing = false;
    }
}

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 ().

Milan Laslop
sumber
0

Coba solusi ini. Saya telah menggunakan penyatuan data untuk tampilan yang mengikat. Anda dapat menggunakan fungsi "findViewById ()" yang umum.

public class ActCPExpense extends BaseActivity implements View.OnClickListener,  {
private static final String TAG = ActCPExpense.class.getSimpleName();
private Context mContext;
private ActCpLossBinding mBinding;

private ViewPagerAdapter adapter;



@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    try {
        setContentView(R.layout.act_cp_loss);
        mBinding = DataBindingUtil.setContentView(this, R.layout.act_cp_loss);
        mContext = ActCPExpense.this;
        initViewsAct();


    } catch (Exception e) {
        LogUtils.LOGE(TAG, e);
    }

}


private void initViewsAct() {

    adapter = new ViewPagerAdapter(getSupportFragmentManager());
    adapter.addFragment(FragmentCPPayee.newInstance(), "Title");
    mBinding.viewpager.setAdapter(adapter);
    mBinding.tab.setViewPager(mBinding.viewpager);



}



@Override
public boolean onOptionsItemSelected(MenuItem itemActUtility) {
    int i = itemActUtility.getItemId();
    if (i == android.R.id.home) {
        onBackPressed();

    } 

    return super.onOptionsItemSelected(itemActUtility);
}

@Override
public void onClick(View view) {
    super.onClick(view);
    int id = view.getId();
    if (id == R.id.btnAdd) {
        addFragment();

    } else if (id == R.id.btnDelete) {
        removeFragment();

    }


}
    private void addFragment(){
    adapter.addFragment(FragmentCPPayee.newInstance("Title");
    adapter.notifyDataSetChanged();
    mBinding.tab.setViewPager(mBinding.viewpager);
}
private void removeFragment(){
    adapter.removeItem(mBinding.viewpager.getCurrentItem());
    mBinding.tab.setViewPager(mBinding.viewpager);
}


class ViewPagerAdapter extends FragmentStatePagerAdapter {
    private final List<Fragment> mFragmentList = new ArrayList<>();
    private final List<String> mFragmentTitleList = new ArrayList<>();


    public ViewPagerAdapter(FragmentManager manager) {
        super(manager);
    }

    @Override
    public int getItemPosition(@NonNull Object object) {
        return PagerAdapter.POSITION_NONE;
    }


    @Override
    public Fragment getItem(int position) {
        return mFragmentList.get(position);
    }

    @Override
    public int getCount() {
        return mFragmentList.size();
    }


    public void addFragment(Fragment fragment, String title) {
        mFragmentList.add(fragment);
        mFragmentTitleList.add(title);

    }

    public void removeItem(int pos) {

        destroyItem(null, pos, mFragmentList.get(pos));
        mFragmentList.remove(pos);
        mFragmentTitleList.remove(pos);
        adapter.notifyDataSetChanged();
        mBinding.viewpager.setCurrentItem(pos - 1, false);
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return "Title " + String.valueOf(position + 1);
    }
}


}
Patel Jaimin
sumber
0

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:

private class ChartPagerAdapter extends FragmentPagerAdapter{
    private ArrayList<Fragment> fragmentList;

    ChartPagerAdapter(FragmentManager fm){
        super(fm);
        fragmentList = new ArrayList<>();
    }

    void setFragments(ArrayList<? extends Fragment> fragments){
        fragmentList.addAll(fragments);
    }

    void clearFragments(){
        for(Fragment fragment:fragmentList)
            getChildFragmentManager().beginTransaction().remove(fragment).commit();
        fragmentList.clear();
    }

    @Override
    public Fragment getItem(int i) {
        return fragmentList.get(i);
    }

    @Override
    public int getCount() {
        return fragmentList.size();
    }

}
Azmi Rutkay BIYIK
sumber
0

saya memecahkan masalah ini dengan langkah-langkah ini

1- gunakan FragmentPagerAdapter

2- di setiap fragmen buat id acak

fragment.id = new Random().nextInt();

3- mengganti getItemPosition di adaptor

@Override
public int getItemPosition(@NonNull Object object) {
    return PagerAdapter.POSITION_NONE;
}

4-override getItemId di adaptor

 @Override
public long getItemId(int position) {
    return mDatasetFragments.get(position).id;
}

5- sekarang hapus kode

 adapter.mDatasetFragments.remove(< item to delete position >);
 adapter.notifyDataSetChanged();

ini bekerja untuk saya, saya harap membantu

Ali Parsa
sumber
0

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

public class FragmentStatePagerAdapterFix extends PagerAdapter {
    private static final String TAG = FragmentStatePagerAdapterFix.class.getSimpleName();
    private static final boolean DEBUG = false;

    private WeakReference<FragmentActivity> wrFragmentActivity;
    private WeakReference<Fragment> wrParentFragment;
    private final FragmentManager mFragmentManager;
    private FragmentTransaction mCurTransaction = null;

    protected ArrayList<Fragment> mFragments = new ArrayList<>();
    protected ArrayList<FragmentState> mFragmentStates = new ArrayList<>();
    protected ArrayList<String> mFragmentTags = new ArrayList<>();
    protected ArrayList<String> mFragmentClassNames = new ArrayList<>();
    protected ArrayList<Bundle> mFragmentArgs = new ArrayList<>();

    private Fragment mCurrentPrimaryItem = null;
    private boolean[] mTempPositionChange;

    @Override
    public int getCount() {
        return mFragmentClassNames.size();
    }

    public FragmentActivity getFragmentActivity() {
        return wrFragmentActivity.get();
    }

    public Fragment getParentFragment() {
        return wrParentFragment.get();
    }

    public FragmentStatePagerAdapterFix(FragmentActivity activity) {
        mFragmentManager = activity.getSupportFragmentManager();
        wrFragmentActivity = new WeakReference<>(activity);
        wrParentFragment = new WeakReference<>(null);
    }

    public FragmentStatePagerAdapterFix(Fragment fragment) {
        mFragmentManager = fragment.getChildFragmentManager();
        wrFragmentActivity = new WeakReference<>(fragment.getActivity());
        wrParentFragment = new WeakReference<>(fragment);
    }

    public void add(Class<? extends android.support.v4.app.Fragment> fragClass) {
        add(fragClass, null, null);
    }

    public void add(Class<? extends android.support.v4.app.Fragment> fragClass, Bundle args) {
        add(fragClass, args, null);
    }

    public void add(Class<? extends android.support.v4.app.Fragment> fragClass, String tag) {
        add(fragClass, null, tag);
    }

    public void add(Class<? extends android.support.v4.app.Fragment> fragClass, Bundle args, String tag) {
        add(fragClass, args, tag, getCount());
    }

    public void add(Class<? extends android.support.v4.app.Fragment> fragClass, Bundle args, String tag, int position) {
        mFragments.add(position, null);
        mFragmentStates.add(position, null);
        mFragmentTags.add(position, tag);
        mFragmentClassNames.add(position, fragClass.getName());
        mFragmentArgs.add(position, args);
        mTempPositionChange = new boolean[getCount()];
    }

    public void remove(int position) {
        if (position < getCount()) {
            mTempPositionChange = new boolean[getCount()];
            for (int i = position; i < mTempPositionChange.length; i++) {
                mTempPositionChange[i] = true;
            }
            mFragments.remove(position);
            mFragmentStates.remove(position);
            mFragmentTags.remove(position);
            mFragmentClassNames.remove(position);
            mFragmentArgs.remove(position);
        }
    }

    public void clear(){
        mFragments.clear();
        mFragmentStates.clear();
        mFragmentTags.clear();
        mFragmentClassNames.clear();
        mFragmentArgs.clear();
    }

    @Override
    public void startUpdate(ViewGroup container) {
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment fragment;
        // If we already have this item instantiated, there is nothing
        // to do.  This can happen when we are restoring the entire pager
        // from its saved state, where the fragment manager has already
        // taken care of restoring the fragments we previously had instantiated.
        fragment = mFragments.get(position);
        if (fragment != null) {
            return fragment;
        }
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
        FragmentState fs = mFragmentStates.get(position);
        if (fs != null) {
            fragment = fs.instantiate(getFragmentActivity(), getParentFragment());
            // Fix bug
            // http://stackoverflow.com/questions/11381470/classnotfoundexception-when-unmarshalling-android-support-v4-view-viewpagersav
            if (fragment.mSavedFragmentState != null) {
                fragment.mSavedFragmentState.setClassLoader(fragment.getClass().getClassLoader());
            }
        }
        if (fragment == null) {
            fragment = Fragment.instantiate(getFragmentActivity(), mFragmentClassNames.get(position), mFragmentArgs.get(position));
        }
        if (DEBUG) {
            Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
        }
        fragment.setMenuVisibility(false);
        fragment.setUserVisibleHint(false);
        mFragments.set(position, fragment);
        mFragmentStates.set(position, null);
        mCurTransaction.add(container.getId(), fragment, mFragmentTags.get(position));
        return fragment;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        Fragment fragment = (Fragment) object;
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
        if (DEBUG) {
            Log.v(TAG, "Removing item #" + position + ": f=" + object
                    + " v=" + ((Fragment) object).getView());
        }
        if (position < getCount()) {
            FragmentState fragmentState = new FragmentState(fragment);
            Fragment.SavedState savedState = mFragmentManager.saveFragmentInstanceState(fragment);
            if (savedState != null) {
                fragmentState.mSavedFragmentState = savedState.mState;
            }
            mFragmentStates.set(position, fragmentState);
            mFragments.set(position, null);
        }
        mCurTransaction.remove(fragment);
    }

    @Override
    public void setPrimaryItem(ViewGroup container, int position, Object object) {
        Fragment fragment = (Fragment) object;
        if (fragment != mCurrentPrimaryItem) {
            if (mCurrentPrimaryItem != null) {
                mCurrentPrimaryItem.setMenuVisibility(false);
                mCurrentPrimaryItem.setUserVisibleHint(false);
            }
            if (fragment != null) {
                fragment.setMenuVisibility(true);
                fragment.setUserVisibleHint(true);
            }
            mCurrentPrimaryItem = fragment;
        }
    }

    @Override
    public void finishUpdate(ViewGroup container) {
        if (mCurTransaction != null) {
            mCurTransaction.commitAllowingStateLoss();
            mCurTransaction = null;
            mFragmentManager.executePendingTransactions();
            // Fix: Fragment is added by transaction. BUT didn't add to FragmentManager's mActive.
            for (Fragment fragment : mFragments) {
                if (fragment != null) {
                    fixActiveFragment(mFragmentManager, fragment);
                }
            }
        }
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return ((Fragment) object).getView() == view;
    }

    @Override
    public Parcelable saveState() {
        Bundle state = null;
        // 目前顯示的 Fragments
        for (int i = 0; i < mFragments.size(); i++) {
            Fragment f = mFragments.get(i);
            if (f != null && f.isAdded()) {
                if (state == null) {
                    state = new Bundle();
                }
                String key = "f" + i;
                mFragmentManager.putFragment(state, key, f);
            }
        }
        if (mFragmentStates.size() > 0) {
            if (state == null) {
                state = new Bundle();
            }
            FragmentState[] fs = new FragmentState[mFragmentStates.size()];
            mFragmentStates.toArray(fs);
            state.putParcelableArray("states_fragment", fs);
        }
        return state;
    }

    @Override
    public void restoreState(Parcelable state, ClassLoader loader) {
        if (state != null) {
            Bundle bundle = (Bundle) state;
            bundle.setClassLoader(loader);
            Parcelable[] fs = bundle.getParcelableArray("states_fragment");
            mFragments.clear();
            mFragmentStates.clear();
            mFragmentTags.clear();
            mFragmentClassNames.clear();
            mFragmentArgs.clear();
            if (fs != null) {
                for (int i = 0; i < fs.length; i++) {
                    FragmentState fragmentState = (FragmentState) fs[i];
                    mFragmentStates.add(fragmentState);
                    if (fragmentState != null) {
                        mFragmentArgs.add(fragmentState.mArguments);
                        mFragmentTags.add(fragmentState.mTag);
                        mFragmentClassNames.add(fragmentState.mClassName);
                    } else {
                        mFragmentArgs.add(null);
                        mFragmentTags.add(null);
                        mFragmentClassNames.add(null);
                    }
                    mFragments.add(null);
                }
            }
            Iterable<String> keys = bundle.keySet();
            for (String key : keys) {
                if (key.startsWith("f")) {
                    int index = Integer.parseInt(key.substring(1));
                    Fragment f = mFragmentManager.getFragment(bundle, key);
                    if (f != null) {
                        f.setMenuVisibility(false);
                        mFragments.set(index, f);
                        mFragmentArgs.set(index, f.mArguments);
                        mFragmentTags.set(index, f.mTag);
                        mFragmentClassNames.set(index, f.getClass().getName());
                    } else {
                        Log.w(TAG, "Bad fragment at key " + key);
                    }
                }
            }
            // If restore will change
            notifyDataSetChanged();
        }
    }

    public static void fixActiveFragment(FragmentManager fragmentManager, Fragment fragment) {
        FragmentManagerImpl fm = (FragmentManagerImpl) fragmentManager;
        if (fm.mActive != null) {
            int index = fragment.mIndex;
            Fragment origin = fm.mActive.get(index);
            if (origin != null) {
                if ((origin.mIndex != fragment.mIndex) || !(origin.equals(fragment))) {
                    Log.e(TAG,
                            String.format("fixActiveFragment: Not Equal! Origin: %s %s, Fragment: %s $s",
                                    origin.getClass().getName(), origin.mIndex,
                                    fragment.getClass().getName(), fragment.mIndex
                            ));
                }
            }
            fm.mActive.set(index, fragment);
        }
    }

    // Fix
    // http://stackoverflow.com/questions/10396321/remove-fragment-page-from-viewpager-in-android
    @Override
    public int getItemPosition(Object object) {
        int index = mFragments.indexOf(object);
        if (index < 0) {
            return PagerAdapter.POSITION_NONE;
        }
        boolean isPositionChange = mTempPositionChange[index];
        int result = PagerAdapter.POSITION_UNCHANGED;
        if (isPositionChange) {
            result = PagerAdapter.POSITION_NONE;
        }
        return result;
    }
}
林奕 忠
sumber
Saya memiliki 2 fragmen mainfragment dan fragmen lainnya. Saya menambahkanfragment lain setelah setiap posisi ke-5 dan tidak ada dalam daftar array jadi bagaimana cara menghapusfragment lain itu jika tidak ada dalam daftar? Saya menggunakan viewpager2
b devloper
tautan github tidak berfungsi
b devloper
-1

Anda bisa mengganti destroyItemmetode tersebut

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    fragmentManager.beginTransaction().remove((Fragment) object).commitNowAllowingStateLoss();
}
nama pengguna
sumber
-9

Saya harap ini dapat membantu apa yang Anda inginkan.

private class MyPagerAdapter extends FragmentStatePagerAdapter {

    //... your existing code

    @Override
    public int getItemPosition(Object object){
        return PagerAdapter.POSITION_UNCHANGED;
    }
}
Yin Khaing
sumber
9
ini adalah kebalikan dari jawaban yang diterima - bagaimana ini bisa berhasil ??
Zordid