Supprimer la page de fragment de ViewPager dans Android

138

J'essaie d'ajouter et de supprimer dynamiquement des fragments d'un ViewPager, en ajoutant des travaux sans aucun problème, mais la suppression ne fonctionne pas comme prévu.

Chaque fois que je souhaite supprimer l'élément actuel, le dernier est supprimé.

J'ai également essayé d'utiliser un FragmentStatePagerAdapter ou de retourner POSITION_NONE dans la méthode getItemPosition de l'adaptateur.

Qu'est-ce que je fais mal?

Voici un exemple de base:

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
la source
2
+1 pour le code source
wizurd
est-il correct d'utiliser un constructeur non par défaut dans MyFragment.java??
codeKiller

Réponses:

95

La solution de Louth n'était pas suffisante pour que les choses fonctionnent pour moi, car les fragments existants n'étaient pas détruits. Motivé par cette réponse , j'ai trouvé que la solution est de remplacer la getItemId(int position)méthode deFragmentPagerAdapter donner un nouvel identifiant unique chaque fois qu'il y a eu un changement dans la position attendue d'un fragment.

Code source:

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

Maintenant, par exemple si vous supprimez un seul onglet ou apportez des modifications à la commande, vous devez appeler notifyChangeInPosition(1)avant d'appelernotifyDataSetChanged() , ce qui garantira que tous les fragments seront recréés.

Pourquoi cette solution fonctionne

Remplacer getItemPosition ():

Lorsqu'il notifyDataSetChanged()est appelé, l'adaptateur appelle la notifyChanged()méthode à ViewPagerlaquelle il est attaché. Le ViewPagervérifie ensuite la valeur renvoyée par l'adaptateur getItemPosition()pour chaque élément, en supprimant les éléments qui retournent POSITION_NONE(voir le code source ), puis en le repeuplant.

Remplacer getItemId ():

Cela est nécessaire pour empêcher l'adaptateur de recharger l'ancien fragment lors du ViewPagerrepeuplement du. Vous pouvez facilement comprendre pourquoi cela fonctionne en regardant le code source pour instantiateItem () dans 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));
    }

Comme vous pouvez le voir, la getItem()méthode n'est appelée que si le gestionnaire de fragments ne trouve aucun fragment existant avec le même ID. Pour moi, cela semble être un bogue que les anciens fragments sont toujours attachés même après avoir notifyDataSetChanged()été appelé, mais la documentation pour ViewPagerindique clairement que:

Notez que cette classe est actuellement en cours de conception et de développement. L'API changera probablement dans les mises à jour ultérieures de la bibliothèque de compatibilité, ce qui nécessitera des modifications du code source des applications lorsqu'elles seront compilées avec la version la plus récente.

J'espère donc que la solution de contournement donnée ici ne sera pas nécessaire dans une future version de la bibliothèque de support.

Tim Rae
la source
1
Merci beaucoup pour cette réponse! Il a été épuisant d'essayer de résoudre ce problème sans utiliser FragmentStatePagerAdapter. Excellente explication aussi. Avez-vous trouvé un moyen de déclencher getItem lorsque vous revenez au fragment? J'ai essayé d'utiliser votre intelligent notifyDataSetChanged () à divers endroits (par exemple onStop) sans succès
u2tall
Je ne comprends pas vraiment votre question ... S'il vous plaît postez une nouvelle question avec le code source si vous ne pouvez pas le comprendre.
Tim Rae
1
2016, cette solution de contournement est toujours nécessaire notifyDataSetChanged()pour fonctionner.
Subin Sebastian
Hou la la! Explication incroyable. J'avais un viewpager assez complexe où je supprime et insère des éléments de manière dynamique et je ne le ferais pas fonctionner sans comprendre tout cela.
rupps
Toujours besoin de cette solution de contournement pour remplacer l'ancien fragment. Dang it.
kopikaokao
291

Le ViewPager ne supprime pas vos fragments avec le code ci-dessus car il charge plusieurs vues (ou fragments dans votre cas) en mémoire. En plus de la vue visible, il charge également la vue de chaque côté de la vue visible. Cela fournit le défilement fluide d'une vue à l'autre qui rend le ViewPager si cool.

Pour obtenir l'effet souhaité, vous devez faire plusieurs choses.

  1. Remplacez FragmentPagerAdapter par FragmentStatePagerAdapter. La raison en est que le FragmentPagerAdapter conservera toutes les vues qu'il charge en mémoire pour toujours. Où le FragmentStatePagerAdapter supprime les vues qui tombent en dehors des vues actuelles et traversables.

  2. Remplacez la méthode d'adaptateur getItemPosition (illustrée ci-dessous). Quand on appellemAdapter.notifyDataSetChanged(); le ViewPager interroge l'adaptateur pour déterminer ce qui a changé en termes de positionnement. Nous utilisons cette méthode pour dire que tout a changé, alors retraitez tout le positionnement de votre vue.

Et voici le code ...

private class MyPagerAdapter extends FragmentStatePagerAdapter {

    //... your existing code

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

}
Louth
la source
1
Ok, celui-ci semble être la solution de contournement la plus simple pour ce problème. Bien que quelques autres solutions de contournement soient abordées dans les rapports de bogue ici: code.google.com/p/android/issues/detail?id=19110 et ici: code.google.com/p/android/issues/detail?id= 19001
Subin Sebastian
@Louth: J'utilise ViewPager avec TabsAdapter, comme étendu de FragmentStatePagerAdapter (par exemple sur la page de référence Google: developer.android.com/reference/android/support/v4/view/… ). Ma question de suivi est la suivante: comment supprimer un onglet en premier lieu? Merci.
gcl1
4
@Louth Lorsque nous appelons notifyDataSetChanged (), le visualiseur crée de nouveaux fragments, mais les plus anciens sont toujours en mémoire. Et mon problème est; ils reçoivent un appel pour leur événement onOptionsItemSelected. Comment puis-je me débarrasser des anciens fragments?
syloc
1
Merci, le simple remplacement de FragmentPagerAdapter par FragmentStatePagerAdapter a résolu mon problème. Après cela, je viens de supprimerAllViews () sur ViewPager et recharger à nouveau les fragments dynamiquement.
Michal
17
Pourquoi renvoyez-vous toujours POSITION_NONE au lieu de vraiment chercher si une vue / un fragment / un objet donné existe toujours et est valide? Cette méthode est censée répondre à la question "hé, puis-je réutiliser celle-ci?" - et toujours en disant "Non!" signifie simplement la recréation de tout! Cela n'est pas nécessaire dans de nombreux cas.
Zordid
14

ma solution de travail pour supprimer la page de fragment de la page de visualisation

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

Et quand j'ai besoin de supprimer une page par index, je le fais

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

Voici l'initialisation de mon adaptateur

MyFragmentAdapter adapter = new MyFragmentAdapter(getSupportFragmentManager(), pages);
viewPager.setAdapter(adapter);
Vasil Valchev
la source
Voulez-vous s'il vous plaît partager le code pour l'ajout d'un fragment?
VVB
Mais tout en ajoutant, il reflète lent après un balayage deux fois / trois fois
VVB
si vous ne supprimez pas d'éléments, il ne renvoie que des éléments, rien de plus ... testez-vous sur vm ou sur un appareil réel?
Vasil Valchev
Essayez d'autres solutions, des réponses avec plus de votes. View pager a un système d'encaissement très complexe pour de meilleures performances sur swip, donc ces hacks ne sont pas gérés par le pager de vue original whit but ...
Vasil Valchev
11

Le fragment doit déjà être supprimé mais le problème était l'état de sauvegarde du visualiseur

Essayer

myViewPager.setSaveFromParentEnabled(false);

Rien n'a fonctionné mais cela a résolu le problème!

À votre santé !

Kathan Shah
la source
2

J'ai eu l'idée de simplement copier le code source android.support.v4.app.FragmentPagerAdpaterdans une classe personnalisée nommée CustumFragmentPagerAdapter. Cela m'a donné la possibilité de modifier le instantiateItem(...)afin que chaque fois qu'il est appelé, il supprime / détruit le fragment actuellement attaché avant d'ajouter le nouveau fragment reçu de getItem()method.

Modifiez simplement le instantiateItem(...)de la manière suivante:

@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
la source
1

Vous pouvez combiner les deux pour mieux:

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
la source
Cela ne fonctionne que si vous supprimez l'onglet le plus à droite, pour supprimer un onglet interne ou l'onglet de gauche, vous devez réorganiser les onglets et renvoyer la nouvelle position comme indiqué dans la documentation developer.android.com/reference/android/support/v4 / view /…
BitByteDog
1

Pour les futurs lecteurs!

Vous pouvez maintenant utiliser ViewPager2 pour ajouter et supprimer dynamiquement un fragment du viewpager.

Référence de l'API du formulaire de devis

ViewPager2 remplace ViewPager, traitant la plupart des problèmes de son prédécesseur, y compris la prise en charge de la mise en page de droite à gauche, l'orientation verticale, les collections de fragments modifiables, etc.

Regardez MutableCollectionFragmentActivity.ktdans googlesample / android-viewpager2 pour un exemple d'ajout, de suppression dynamique de fragments du viewpager.


Pour ton information:

Des articles:

Référence API

Notes de version

Samples Repo: https://github.com/googlesamples/android-viewpager2

utilisateur158
la source
J'ai 2 fragments mainfragment et otherfragment. J'ajoute un autre fragment après chaque 5ème position et il n'est pas dans l'arraylist, alors comment supprimer cet autre fragment s'il ne figure pas dans la liste? J'utilise viewpager2
b devloper le
0

La réponse de Louth fonctionne bien. Mais je ne pense pas que toujours renvoyer POSITION_NONE soit une bonne idée. Parce que POSITION_NONE signifie que le fragment doit être détruit et un nouveau fragment sera créé. Vous pouvez vérifier cela dans la fonction dataSetChanged dans le code source de ViewPager.

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

Je pense donc que vous feriez mieux d'utiliser une arraylist de lowReference pour enregistrer tous les fragments que vous avez créés. Et lorsque vous ajoutez ou supprimez une page, vous pouvez obtenir la bonne position à partir de votre propre arraylist.

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

Selon les commentaires, getItemPosition est appelé lorsque la vue hôte tente de déterminer si la position d'un élément a changé. Et la valeur de retour signifie sa nouvelle position.

Mais ce n'est pas suffisant. Nous avons encore une étape importante à franchir. Dans le code source de FragmentStatePagerAdapter, il existe un tableau nommé "mFragments" qui met en cache les fragments qui ne sont pas détruits. Et dans la fonction instantiateItem.

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

Il a renvoyé le fragment mis en cache directement lorsqu'il trouve que le fragment mis en cache n'est pas nul. Il y a donc un problème. À partir de l'exemple, supprimons une page à la position 2. Premièrement, nous supprimons ce fragment de notre propre arraylist de référence. donc dans getItemPosition il retournera POSITION_NONE pour ce fragment, puis ce fragment sera détruit et supprimé de "mFragments".

 mFragments.set(position, null);

Maintenant, le fragment à la position 3 sera à la position 2. Et l'élément instancié avec la position 3 du paramètre sera appelé. À ce stade, le troisième élément de "mFramgents" n'est pas nul, il retournera donc directement. Mais en fait, il a renvoyé le fragment à la position 2. Ainsi, lorsque nous passerons à la page 3, nous y trouverons une page vide.

Pour contourner ce problème. Mon conseil est que vous pouvez copier le code source de FragmentStatePagerAdapter dans votre propre projet, et lorsque vous ajoutez ou supprimez des opérations, vous devez ajouter et supprimer des éléments dans l'arraylist "mFragments".

Les choses seront plus simples si vous utilisez simplement PagerAdapter au lieu de FragmentStatePagerAdapter. Bonne chance.

passant
la source
0

ajouter ou supprimer dynamiquement un fragment dans Viewpager.
Appelez setupViewPager (viewPager) au début de l'activité. Pour charger différents fragments, appelez setupViewPagerCustom (viewPager). par exemple sur un bouton cliquez sur appel: 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
la source
0

J'ai eu quelques problèmes avec FragmentStatePagerAdapter. Après avoir supprimé un élément:

  • il y avait un autre élément utilisé pour un poste (un élément qui n'appartenait pas au poste mais à un poste à côté)
  • ou un fragment n'a pas été chargé (il n'y avait qu'un arrière-plan vierge visible sur cette page)

Après de nombreuses expériences, j'ai trouvé la solution suivante.

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

Cette solution n'utilise un hack qu'en cas de suppression d'un élément. Sinon (par exemple lors de l'ajout d'un article), il conserve la propreté et les performances d'un code d'origine.

Bien sûr, de l'extérieur de l'adaptateur, vous n'appelez que addItem / removeItem, pas besoin d'appeler notifyDataSetChanged ().

Milan Laslop
la source
0

Essayez cette solution. J'ai utilisé la liaison de données pour la vue de liaison. Vous pouvez utiliser la fonction commune "findViewById ()".

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
la source
0

J'ai ajouté une fonction "clearFragments" et j'ai utilisé cette fonction pour effacer l'adaptateur avant de définir les nouveaux fragments. Cela appelle les actions de suppression appropriées des fragments. Ma classe pagerAdapter:

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
la source
0

j'ai résolu ce problème par ces étapes

1- utilisez FragmentPagerAdapter

2- dans chaque fragment créer un identifiant aléatoire

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

3- Remplacer getItemPosition dans l'adaptateur

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

4-override getItemId dans l'adaptateur

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

5- maintenant supprimer le code est

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

cela a fonctionné pour moi j'espère aider

Ali Parsa
la source
0

Mon code de version finale, corrigé TOUS les bogues. Cela m'a pris 3 jours

Mise à jour 18/07/2020: j'avais changé une grande partie du code source et corrigé tant de bugs, mais je ne promets pas que cela fonctionne encore aujourd'hui.

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;
    }
}
林奕 忠
la source
J'ai 2 fragments mainfragment et otherfragment. J'ajoute un autre fragment après chaque 5ème position et il n'est pas dans l'arraylist, alors comment supprimer cet autre fragment s'il ne figure pas dans la liste? J'utilise viewpager2
b devloper le
lien github ne fonctionne pas
b devloper
-1

Vous pouvez simplement remplacer la destroyItemméthode

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

J'espère que cela peut aider ce que vous voulez.

private class MyPagerAdapter extends FragmentStatePagerAdapter {

    //... your existing code

    @Override
    public int getItemPosition(Object object){
        return PagerAdapter.POSITION_UNCHANGED;
    }
}
Yin Khaing
la source
9
c'est exactement le contraire de la réponse acceptée - comment cela peut-il fonctionner?
Zordid