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>
MyFragment.java
??Réponses:
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:
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 lanotifyChanged()
méthode àViewPager
laquelle il est attaché. LeViewPager
vérifie ensuite la valeur renvoyée par l'adaptateurgetItemPosition()
pour chaque élément, en supprimant les éléments qui retournentPOSITION_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
ViewPager
repeuplement du. Vous pouvez facilement comprendre pourquoi cela fonctionne en regardant le code source pour instantiateItem () dansFragmentPagerAdapter
.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 avoirnotifyDataSetChanged()
été appelé, mais la documentation pourViewPager
indique clairement que: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.
la source
notifyDataSetChanged()
pour fonctionner.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.
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.
Remplacez la méthode d'adaptateur getItemPosition (illustrée ci-dessous). Quand on appelle
mAdapter.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 ...
la source
ma solution de travail pour supprimer la page de fragment de la page de visualisation
Et quand j'ai besoin de supprimer une page par index, je le fais
Voici l'initialisation de mon adaptateur
la source
Le fragment doit déjà être supprimé mais le problème était l'état de sauvegarde du visualiseur
Essayer
Rien n'a fonctionné mais cela a résolu le problème!
À votre santé !
la source
J'ai eu l'idée de simplement copier le code source
android.support.v4.app.FragmentPagerAdpater
dans une classe personnalisée nomméeCustumFragmentPagerAdapter
. Cela m'a donné la possibilité de modifier leinstantiateItem(...)
afin que chaque fois qu'il est appelé, il supprime / détruit le fragment actuellement attaché avant d'ajouter le nouveau fragment reçu degetItem()
method.Modifiez simplement le
instantiateItem(...)
de la manière suivante:la source
Vous pouvez combiner les deux pour mieux:
la source
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
Regardez
MutableCollectionFragmentActivity.kt
dans 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
la source
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.
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.
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.
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".
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.
la source
la source
J'ai eu quelques problèmes avec
FragmentStatePagerAdapter
. Après avoir supprimé un élément:Après de nombreuses expériences, j'ai trouvé la solution suivante.
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 ().
la source
Essayez cette solution. J'ai utilisé la liaison de données pour la vue de liaison. Vous pouvez utiliser la fonction commune "findViewById ()".
la source
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:
la source
j'ai résolu ce problème par ces étapes
1- utilisez FragmentPagerAdapter
2- dans chaque fragment créer un identifiant aléatoire
3- Remplacer getItemPosition dans l'adaptateur
4-override getItemId dans l'adaptateur
5- maintenant supprimer le code est
cela a fonctionné pour moi j'espère aider
la source
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
la source
Vous pouvez simplement remplacer la
destroyItem
méthodela source
J'espère que cela peut aider ce que vous voulez.
la source