Android'de ViewPager Fragment Page Kaldır


138

Dinamik olarak eklemek ve bir ViewPager Fragmanları kaldırmak çalışıyorum, sorunsuz bir şekilde ekleyerek çalışıyor, ancak kaldırma beklendiği gibi çalışmıyor.

Geçerli öğeyi her kaldırmak istediğimde sonuncusu kaldırılır.

Ayrıca bir FragmentStatePagerAdapter kullanmaya veya adaptörün getItemPosition yönteminde POSITION_NONE döndürmeye çalıştım.

Neyi yanlış yapıyorum?

İşte temel bir örnek:

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>

2
Kaynak kodu için +1
wizurd

varsayılan olmayan kurucu kullanmak doğru mu MyFragment.java?
codeKiller

Yanıtlar:


95

Mevcut parçaların yok edilmemesi nedeniyle, Louth'un çözümü benim için işleri işler hale getirmek için yeterli değildi. Bu cevabı motive ederek , bir Parçanın beklenen konumunda her değişiklik olduğunda çözümün yeni bir benzersiz kimlik verme getItemId(int position)yöntemini geçersiz kılmak olduğunu buldum FragmentPagerAdapter.

Kaynak kodu:

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

Şimdi, örneğin tek bir sekmeyi silerseniz veya siparişte bir değişiklik yaparsanız, aramadan notifyChangeInPosition(1)önce aramalısınıznotifyDataSetChanged() , bu da tüm Parçaların yeniden oluşturulmasını sağlayacaktır.

Bu çözüm neden işe yarıyor?

GetItemPosition () geçersiz kılınıyor:

Arandığında notifyDataSetChanged(), bağdaştırıcı bağlı olduğu notifyChanged()yöntemi çağırır ViewPager. Ardından , her bir öğe için ViewPagerbağdaştırıcının döndürdüğü değeri denetler, geri getItemPosition()dönen POSITION_NONE( kaynak koduna bakın ) ve sonra yeniden doldurulan öğeleri kaldırır .

GetItemId () geçersiz kılınıyor:

Bu, yeniden doldururken bağdaştırıcının eski parçayı yeniden yüklemesini önlemek için gereklidir ViewPager. İn instantiateItem () için kaynak koduna bakarak bunun neden işe yaradığını kolayca anlayabilirsiniz 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));
    }

Gördüğünüz gibi, getItem()yöntem yalnızca parça yöneticisi aynı kimliğe sahip mevcut parça bulamazsa çağrılır. Bana göre, eski parçaların notifyDataSetChanged()çağrıldıktan sonra bile hala bağlı olduğu bir hata gibi görünüyor , ancak belgelerinin ViewPageraçıkça belirttiği gibi:

Bu sınıfın şu anda erken tasarım ve geliştirme aşamasında olduğunu unutmayın. API, uyumluluk kitaplığının sonraki güncellemelerinde büyük olasılıkla değişecek ve daha yeni sürüme karşı derlendiklerinde uygulamaların kaynak kodunda değişiklik yapılmasını gerektirecektir.

Bu yüzden umarım burada verilen geçici çözüm, destek kitaplığının gelecekteki bir sürümünde gerekli olmayacaktır.


1
Bu cevap için çok teşekkürler! FragmentStatePagerAdapter kullanmadan bu sorunu çözmeye çalışırken yorucu olmuştur. Çok büyük açıklama. Parçaya geri dönerken getItem'i tetiklemenin bir yolunu buldunuz mu? Ben akıllı notifyDataSetChanged () çeşitli yerlerde (yani onStop) başarıyla kullanmadan denedim
u2tall

Sorunuzu gerçekten anlamıyorum ... Eğer çözemiyorsanız, lütfen kaynak kodunu içeren yeni bir soru gönderin.
Tim Rae

1
2016, bu geçici çözümün çalışması için hala gereklidir notifyDataSetChanged().
Subin Sebastian

Vaov! İnanılmaz bir açıklama. Öğeleri dinamik olarak kaldırıp eklediğim ve tüm bunları anlamadan işe yaramayacağım oldukça karmaşık bir görünümüm vardı.
16:34, rupps

Eski parçayı değiştirmek için hala bu geçici çözüme ihtiyacınız var. Lanet olsun.
kopikaokao

291

ViewPager, yukarıdaki kodla parçalarınızı kaldırmaz, çünkü belleğe birkaç görünüm (veya davanızdaki parçalar) yükler. Görünür görünüme ek olarak, görünümü görünenin her iki tarafına da yükler. Bu, ViewPager'i çok havalı yapan görünümden görünüme yumuşak bir kaydırma sağlar.

İstediğiniz efekti elde etmek için birkaç şey yapmanız gerekir.

  1. FragmentPagerAdapter öğesini bir FragmentStatePagerAdapter olarak değiştirin. Bunun nedeni, FragmentPagerAdapter öğesinin belleğe yüklediği tüm görünümleri sonsuza kadar saklayabilmesidir. FragmentStatePagerAdapter öğesinin geçerli ve izlenebilir görünümlerin dışında kalan görünümleri kullandığı yer.

  2. GetItemPosition bağdaştırıcı yöntemini geçersiz kılın (aşağıda gösterilmiştir). mAdapter.notifyDataSetChanged();ViewPager'i aradığımızda , konumlandırma açısından neyin değiştiğini belirlemek için adaptörü sorgular. Bu yöntemi, her şeyin değiştiğini söylemek için kullanıyoruz, böylece tüm görünüm konumunuzu yeniden işleyin.

Ve işte kod ...

private class MyPagerAdapter extends FragmentStatePagerAdapter {

    //... your existing code

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

}

1
Tamam, bu sorun için en kolay çözüm gibi görünüyor. Buradaki hata raporlarında birkaç başka çözüm tartışılsa da: code.google.com/p/android/issues/detail?id=19110 ve burada: code.google.com/p/android/issues/detail?id= 19001
Subin Sebastian

@Louth: FragmentStatePagerAdapter ile genişletilen TabsAdapter ile ViewPager kullanıyorum (örneğin Google ref sayfasındaki örnek: developer.android.com/reference/android/support/v4/view/… ). Takip eden sorum şu: Bir sekmeyi en başta nasıl silebilirsiniz? Teşekkürler.
gcl1

4
@Louth notifyDataSetChanged () öğesini çağırdığımızda viewpager yeni parçalar oluşturur, ancak daha eski olanlar hala bellektir. Benim sorunum; onOptionsItemSelected olayları için bir çağrı alırlar. Eski parçalardan nasıl kurtulabilirim?
syloc

1
Teşekkür ederim, sadece FragmentPagerAdapter yerine bir FragmentStatePagerAdapter değiştirmek sorunumu çözdü. Bundan sonra ViewPager üzerinde sadece kaldırAllViews () ve tekrar dinamik olarak parçaları yeniden.
Michal

17
Belirli bir görünüm / parça / nesne hala mevcut ve geçerliyse, neden gerçekten bakmak yerine POSITION_NONE döndürüyorsunuz? Bu yöntemin "hey, bunu tekrar kullanabilir miyim?" - ve her zaman "Hayır!" sadece her şeyin yeniden yaratılması anlamına gelir! Bu birçok durumda gereksizdir.
Zordid

14

görüntüleme sayfasından parça sayfasını kaldırmak için çalışma çözümüm

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

Ve dizin tarafından bazı sayfa kaldırmak gerektiğinde bunu yapmak

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

İşte benim adaptör başlatım

MyFragmentAdapter adapter = new MyFragmentAdapter(getSupportFragmentManager(), pages);
viewPager.setAdapter(adapter);

Parça eklemek için lütfen kodu paylaşır mısınız?
VVB

Ama eklerken
kaydırdıktan

eğer öğeleri kaldırmıyorsanız, sadece öğeyi döndürür, başka bir şey değil ... vm veya gerçek cihaz üzerinde test mi yapıyorsunuz?
Vasil Valchev

Diğer çözümleri deneyin, daha fazla oy verin. Görünüm çağrı cihazı, swip üzerinde daha iyi performans için çok karmaşık bir para çekme sistemine sahiptir, bu nedenle bu hackler orijinal görünüm çağrı cihazı beyaz amaç tarafından ele alınmaz ...
Vasil Valchev

11

Parça zaten kaldırılmış olmalı, ancak sorun viewpager kaydetme durumuydu

Deneyin

myViewPager.setSaveFromParentEnabled(false);

Hiçbir şey işe yaramadı ama bu sorunu çözdü!

Şerefe!


2

Sadece kaynak kodu adlı android.support.v4.app.FragmentPagerAdpaterözel bir sınıfa kopyalamak fikri vardı CustumFragmentPagerAdapter. Bu, instantiateItem(...)her çağrıldığında, getItem()yöntemden alınan yeni parçayı eklemeden önce bağlı olan parçayı kaldırır / yok eder .

Basitçe instantiateItem(...)aşağıdaki şekilde değiştirin :

@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;
}

1

Her ikisini de daha iyi için birleştirebilirsiniz:

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.

}

Bu yalnızca en sağdaki sekmeyi siliyorsanız, bir iç sekmeyi veya sol sekmeyi silmek için çalışır, sekmeleri yeniden sıralamanız ve belge geliştiricisinde
BitByteDog

1

Gelecekteki okuyucular için!

Artık parçayı viewpager'dan dinamik olarak eklemek ve kaldırmak için ViewPager2'yi kullanabilirsiniz .

Form API'sı başvurusu

ViewPager2, sağdan sola mizanpaj desteği, dikey yönlendirme, değiştirilebilir Parça koleksiyonları vb.

En göz atın MutableCollectionFragmentActivity.ktiçinde googlesample / android-viewpager2 ekleyerek viewpager dinamik parçası çıkarıldıktan bir örnek için.


Bilginize:

Nesne:

API referansı

Sürüm notları

Örnekler Repo: https://github.com/googlesamples/android-viewpager2


Ben 2 parça mainfragment ve diğer fragman var. Ben her 5. pozisyondan sonra otherfragment ekliyorum ve arraylist içinde değil yani listede değilse bu otherfragment nasıl kaldırılır? Viewpager2 kullanıyorum
b devloper

0

Louth'un cevabı iyi çalışıyor. Ama POSITION_NONE dönmek her zaman iyi bir fikir olduğunu sanmıyorum. Çünkü POSITION_NONE, parçanın yok edilmesi gerektiği ve yeni bir parçanın oluşturulacağı anlamına gelir. DataSetChanged işlevinde ViewPager'in kaynak kodunda olup olmadığını kontrol edebilirsiniz.

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

Bu yüzden, yarattığınız tüm parçaları kaydetmek için bir zayıf referans dizisi kullanmanız daha iyi olur. Bir sayfa eklediğinizde veya kaldırdığınızda, kendi arraylistinizden doğru pozisyonu alabilirsiniz.

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

Yorumlara göre, ana bilgisayar görünümü bir öğenin konumunun değişip değişmediğini belirlemeye çalışırken getItemPosition çağrılır. Ve dönüş değeri yeni konumu anlamına gelir.

Ancak bu yeterli değildir. Hala atmamız gereken önemli bir adım var. FragmentStatePagerAdapter kaynak kodunda, "mFragments" adlı bir dizi yok olmayan parçaları önbelleğe alır. Ve instantiateItem işlevinde.

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

Önbelleğe alınmış parçanın boş olmadığını bulduğunda önbelleğe alınmış parçayı doğrudan döndürdü. Yani bir sorun var. Örneğin, konum 2'deki bir sayfayı silelim, Öncelikle, bu parçayı kendi referans dizilimimizden kaldırıyoruz. getItemPosition'da bu parça için POSITION_NONE döndürür ve sonra bu parça yok edilir ve "mFragments" tan kaldırılır.

 mFragments.set(position, null);

Şimdi konum 3'teki parça konum 2'de olacaktır. Param konumu 3 olan örnekleme çağrılır. Şu anda, "mFramgents" içindeki üçüncü öğe boş değil, bu yüzden doğrudan geri dönecek. Ama aslında geri döndüğü kısım 2. pozisyondaki parçadır. Yani 3. sayfaya döndüğümüzde boş bir sayfa bulacağız.

Bu soruna geçici bir çözüm bulmak için. Benim tavsiyem FragmentStatePagerAdapter kaynak kodunu kendi projenize kopyalayabilmeniz ve işlemleri eklediğinizde veya kaldırdığınızda, "mFragments" dizilişinde eleman eklemeniz ve kaldırmanız gerekir.

FragmentStatePagerAdapter yerine PagerAdapter kullanıyorsanız işler daha kolay olacaktır. İyi şanslar.


0

viewpager'a dinamik olarak parça ekleyin veya kaldırın.
Etkinlik başlangıcında setupViewPager'i (viewPager) çağırın. Farklı bir parça yüklemek için setupViewPagerCustom (viewPager) çağrısını yapın. örneğin düğme tıklama çağrısında: 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);
    }
}

0

Bazı sorunlarım vardı FragmentStatePagerAdapter. Bir öğeyi kaldırdıktan sonra:

  • bir pozisyon için kullanılan başka bir eşya daha vardı (pozisyona ait olmayan, fakat onun yanında bir pozisyona ait olan bir eşya)
  • veya bir parça yüklenmedi (bu sayfada yalnızca boş bir arka plan görüldü)

Birçok deneyden sonra, aşağıdaki çözümü buldum.

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

Bu çözüm yalnızca bir öğenin kaldırılması durumunda bir saldırı kullanır. Aksi takdirde (örn. Bir öğe eklerken) orijinal kodun temizliğini ve performansını korur.

Elbette, bağdaştırıcının dışından yalnızca addItem / removeItem'i çağırırsınız, notifyDataSetChanged () öğesini çağırmanıza gerek yoktur.


0

Bu çözümü deneyin. Bağlama görünümü için veri bağlama kullandım. Ortak "findViewById ()" işlevini kullanabilirsiniz.

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


}

0

Bir fonksiyon "clearFragments" ekledim ve yeni fragmanları ayarlamadan önce adaptörü temizlemek için bu fonksiyonu kullandım. Bu, Parçaların uygun kaldırma eylemlerini çağırır. PagerAdapter sınıfım:

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

}

0

bu sorunu bu adımlarla çözdüm

1- FragmentPagerAdapter kullanın

2- Her parçada rastgele bir kimlik oluşturun

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

3- Bağdaştırıcıdaki getItemPosition'ı geçersiz kılma

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

Bağdaştırıcıda 4 geçersiz kılma getItemId

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

5- şimdi kodu sil

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

bu benim için çalıştı umarım yardım eder


0

Son sürüm kodum, TÜM hataları düzeltti. 3 gün sürdü

Updated 2020/07/18: Kaynak kodunu çok değiştirmiştim ve bu kadar çok hatayı düzelttim, ama bugün hala çalışacağına söz vermiyorum.

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

Ben 2 parça mainfragment ve diğer fragman var. Ben her 5. pozisyondan sonra otherfragment ekliyorum ve arraylist içinde değil yani listede değilse bu otherfragment nasıl kaldırılır? Viewpager2 kullanıyorum
b devloper

github bağlantı çalışmıyor
b devloper

-1

Sadece destroyItemyöntemi geçersiz kılabilirsiniz

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

-9

Umarım bu istediğine yardımcı olabilir.

private class MyPagerAdapter extends FragmentStatePagerAdapter {

    //... your existing code

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

9
bu kabul edilen cevabın tam tersidir - bu nasıl çalışabilir?
Zordid
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.