J'utilise ViewPager de la bibliothèque de compatibilité. Je l'ai réussi à afficher plusieurs vues que je peux parcourir.
Cependant, j'ai du mal à trouver comment mettre à jour le ViewPager avec un nouvel ensemble de vues.
J'ai essayé toutes sortes de choses comme appeler mAdapter.notifyDataSetChanged()
, mViewPager.invalidate()
même créer un nouvel adaptateur chaque fois que je veux utiliser une nouvelle liste de données.
Rien n'a aidé, les vues de texte restent inchangées par rapport aux données d'origine.
Mise à jour: j'ai fait un petit projet de test et j'ai presque pu mettre à jour les vues. Je vais coller la classe ci-dessous.
Ce qui ne semble pas être mis à jour est cependant la 2ème vue, le «B» reste, il devrait afficher «Y» après avoir appuyé sur le bouton de mise à jour.
public class ViewPagerBugActivity extends Activity {
private ViewPager myViewPager;
private List<String> data;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
data = new ArrayList<String>();
data.add("A");
data.add("B");
data.add("C");
myViewPager = (ViewPager) findViewById(R.id.my_view_pager);
myViewPager.setAdapter(new MyViewPagerAdapter(this, data));
Button updateButton = (Button) findViewById(R.id.update_button);
updateButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
updateViewPager();
}
});
}
private void updateViewPager() {
data.clear();
data.add("X");
data.add("Y");
data.add("Z");
myViewPager.getAdapter().notifyDataSetChanged();
}
private class MyViewPagerAdapter extends PagerAdapter {
private List<String> data;
private Context ctx;
public MyViewPagerAdapter(Context ctx, List<String> data) {
this.ctx = ctx;
this.data = data;
}
@Override
public int getCount() {
return data.size();
}
@Override
public Object instantiateItem(View collection, int position) {
TextView view = new TextView(ctx);
view.setText(data.get(position));
((ViewPager)collection).addView(view);
return view;
}
@Override
public void destroyItem(View collection, int position, Object view) {
((ViewPager) collection).removeView((View) view);
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Parcelable saveState() {
return null;
}
@Override
public void restoreState(Parcelable arg0, ClassLoader arg1) {
}
@Override
public void startUpdate(View arg0) {
}
@Override
public void finishUpdate(View arg0) {
}
}
}
la source
Réponses:
Il existe plusieurs façons d'y parvenir.
La première option est plus facile, mais un peu plus inefficace.
Remplacez
getItemPosition
votrePagerAdapter
comme ceci:De cette façon, lorsque vous appelez
notifyDataSetChanged()
, le téléavertisseur de vue supprimera toutes les vues et les rechargera toutes. Ainsi, l'effet de rechargement est obtenu.La deuxième option, suggérée par Alvaro Luis Bustamante (anciennement alvarolb) , est de
setTag()
procédéinstantiateItem()
lors de l' instanciation d' une nouvelle vue. Ensuite, au lieu d'utilisernotifyDataSetChanged()
, vous pouvez utiliserfindViewWithTag()
pour trouver la vue que vous souhaitez mettre à jour.La deuxième approche est très flexible et très performante. Félicitations à alvarolb pour la recherche originale.
la source
Je ne pense pas qu'il y ait de bug dans le
PagerAdapter
. Le problème est que comprendre comment cela fonctionne est un peu complexe. En regardant les solutions expliquées ici, il y a un malentendu et donc une mauvaise utilisation des vues instanciées de mon point de vue.Les derniers jours avec lesquels j'ai travaillé
PagerAdapter
etViewPager
, et j'ai trouvé ce qui suit:La
notifyDataSetChanged()
méthode sur lePagerAdapter
notifiera uniquementViewPager
que les pages sous-jacentes ont changé. Par exemple, si vous avez créé / supprimé des pages de façon dynamique (en ajoutant ou en supprimant des éléments de votre liste), leViewPager
devrait s'en occuper. Dans ce cas, je pense que leViewPager
détermine si une nouvelle vue doit être supprimée ou instanciée à l'aide des méthodesgetItemPosition()
etgetCount()
.Je pense que
ViewPager
, après unnotifyDataSetChanged()
appel prend ses vues des enfants et vérifie leur position avec legetItemPosition()
. Si pour une vue enfant, cette méthode revientPOSITION_NONE
, leViewPager
comprend que la vue a été supprimée, en appelantdestroyItem()
et en supprimant cette vue.De cette façon, remplacer
getItemPosition()
pour toujours revenirPOSITION_NONE
est complètement faux si vous ne souhaitez mettre à jour que le contenu des pages, car les vues précédemment créées seront détruites et de nouvelles seront créées à chaque appelnotifyDatasetChanged()
. Cela peut sembler ne pas être si mal pour quelquesTextView
secondes, mais lorsque vous avez des vues complexes, comme ListViews remplies à partir d'une base de données, cela peut être un vrai problème et un gaspillage de ressources.Il existe donc plusieurs approches pour modifier efficacement le contenu d'une vue sans avoir à supprimer et instancier à nouveau la vue. Cela dépend du problème que vous souhaitez résoudre. Mon approche consiste à utiliser la
setTag()
méthode pour toute vue instanciée dans lainstantiateItem()
méthode. Ainsi, lorsque vous souhaitez modifier les données ou invalider la vue dont vous avez besoin, vous pouvez appeler lafindViewWithTag()
méthode sur leViewPager
pour récupérer la vue précédemment instanciée et la modifier / l'utiliser comme vous le souhaitez sans avoir à supprimer / créer une nouvelle vue à chaque fois que vous le souhaitez. pour mettre à jour une valeur.Imaginez par exemple que vous avez 100 pages avec 100
TextView
s et que vous ne souhaitez mettre à jour qu'une seule valeur périodiquement. Avec les approches expliquées précédemment, cela signifie que vous supprimez et instanciez 100TextView
s à chaque mise à jour. Cela n'a aucun sens...la source
setTag
dans la méthodeonInstantiateItem
, souhaitez-vous mettre à jour cette réponse avec du codage? Merci.Changez
FragmentPagerAdapter
pourFragmentStatePagerAdapter
.Remplacez la
getItemPosition()
méthode et retournezPOSITION_NONE
.Finalement, il écoutera
notifyDataSetChanged()
le téléavertisseur à l' écran.la source
La réponse donnée par alvarolb est certainement la meilleure façon de le faire. En s'appuyant sur sa réponse, un moyen simple de mettre en œuvre cela consiste simplement à stocker les vues actives par position:
Puis une fois en surchargeant la
notifyDataSetChanged
méthode, vous pouvez rafraîchir les vues ...Vous pouvez réellement utiliser un code similaire dans
instantiateItem
etnotifyDataSetChanged
pour actualiser votre vue. Dans mon code, j'utilise exactement la même méthode.la source
Eu le même problème. Pour moi, cela a fonctionné pour étendre FragmentStatePagerAdapter et remplacer les méthodes ci-dessous:
la source
Après des heures de frustration en essayant toutes les solutions ci-dessus pour surmonter ce problème et en essayant également de nombreuses solutions sur d'autres questions similaires comme celle-ci , ceci et cela qui ont échoué avec moi pour résoudre ce problème et pour faire
ViewPager
détruire l'ancienFragment
et remplir lepager
avec le nouveau parFragment
. J'ai résolu le problème comme suit:1) Faites en sorte que la
ViewPager
classe s'étendeFragmentPagerAdapter
comme suit:2) Créez un article pour le
ViewPager
qui stocke letitle
et lefragment
suivant:3) Faites en
ViewPager
sorte que le constructeur de la prise monFragmentManager
instance le stocke dans monclass
comme suit:4) Créer une méthode pour définir les re-
adapter
données avec les nouvelles données en supprimant toutes les précédentesfragment
dufragmentManager
même directement pour laadapter
pour définir la nouvellefragment
de la nouvelle liste à nouveau comme suit:5) À partir du conteneur
Activity
ouFragment
ne réinitialisez pas l'adaptateur avec les nouvelles données. Définissez les nouvelles données via la méthodesetPagerItems
avec les nouvelles données comme suit:J'espère que ça aide.
la source
J'ai eu le même problème et ma solution utilise
FragmentPagerAdapter
en remplaçantFragmentPagerAdapter#getItemId(int position)
:Par défaut, cette méthode renvoie la position de l'élément. Je suppose que
ViewPager
vérifie si aitemId
été modifié et recrée la page uniquement si elle l'est. Mais la versionitemId
non remplacée renvoie la même position que même si la page est réellement différente, et ViewPager ne définit pas que la page est remplacée et doit être recréée.Pour l'utiliser,
long id
est nécessaire pour chaque page. Normalement, il devrait être unique, mais je suggère, dans ce cas, qu'il devrait simplement être différent de la valeur précédente pour la même page. Ainsi, il est possible d'utiliser ici un compteur continu dans l'adaptateur ou des entiers aléatoires (avec une large distribution).Je pense que c'est une manière plus cohérente d'utiliser plutôt les balises de vue mentionnées comme solution dans ce sujet. Mais probablement pas pour tous les cas.
la source
PagerAdapter
. De quelle classe s'agit-il?FragmentPagerAdapter
J'ai trouvé une décision très intéressante de ce problème. Au lieu d'utiliser FragmentPagerAdapter , qui garde en mémoire tous les fragments, nous pouvons utiliser FragmentStatePagerAdapter ( android.support.v4.app.FragmentStatePagerAdapter ), qui recharge le fragment à chaque fois, lorsque nous le sélectionnons.
Les réalisations des deux adaptateurs sont identiques. Donc, nous avons juste besoin de changer " étendre FragmentPagerAdapter " sur " étendre FragmentStatePagerAdapter "
la source
Après beaucoup de recherches sur ce problème, j'ai trouvé une très bonne solution qui, selon moi, est la bonne façon de procéder. Essentiellement, instantiateItem n'est appelé que lorsque la vue est instanciée et plus jamais sauf si la vue est détruite (c'est ce qui se produit lorsque vous remplacez la fonction getItemPosition pour renvoyer POSITION_NONE). Au lieu de cela, vous voulez enregistrer les vues créées et de les mettre à jour dans l'adaptateur, de générer une fonction get pour que quelqu'un d'autre puisse la mettre à jour ou une fonction set qui met à jour l'adaptateur (mon préféré).
Donc, dans votre MyViewPagerAdapter ajoutez une variable comme:
un dans votre instantiateItem:
Ainsi, de cette façon, vous pouvez créer une fonction qui mettra à jour votre vue:
J'espère que cela t'aides!
la source
instantiateItem
c'est pourquoi mes vues ne se mettaient pas à jour. D'après votre réponse, je l'ai réalisé. +1Deux ans et demi après que le PO a posé sa question, cette question est toujours, eh bien, toujours un problème. Il est évident que la priorité de Google à ce sujet n'est pas particulièrement élevée, donc plutôt que de trouver un correctif, j'ai trouvé une solution de contournement. La grande percée pour moi a été de découvrir quelle était la véritable cause du problème (voir la réponse acceptée dans cet article ). Une fois qu'il était évident que le problème était que les pages actives n'étaient pas correctement actualisées, ma solution de contournement était évidente:
Dans mon fragment (les pages):
Dans mon activité, où je fais le chargement des pages:
Après cela, lorsque vous rechargez un deuxième ensemble de pages, le bogue entraînera toujours l'affichage des anciennes données par certaines. Cependant, ils seront maintenant actualisés et vous verrez les nouvelles données - vos utilisateurs ne sauront pas que la page était jamais incorrecte car cette actualisation se produira avant de voir la page.
J'espère que cela aide quelqu'un!
la source
Toutes ces solutions ne m'ont pas aidé. j'ai donc trouvé une solution de travail: vous pouvez à
setAdapter
chaque fois, mais ce n'est pas suffisant. vous devez le faire avant de changer d'adaptateur:et après cela:
la source
ViewPager
fragment intérieur, donc remplacéslideShowPagerAdapter.getFragmentManager()
pargetChildFragmentManager()
. PeutgetFragmentManager()
- être vous aidera dans votre cas. J'ai utiliséFragmentPagerAdapter
, nonFragmentStatePagerAdapter
. Voir également stackoverflow.com/a/25994654/2914140 pour une méthode hacky.Une manière beaucoup plus simple: utilisez un
FragmentPagerAdapter
, et enveloppez vos vues paginées sur des fragments. Ils sont mis à jourla source
Merci rui.araujo et Alvaro Luis Bustamante. Au début, j'essaie d'utiliser la voie de rui.araujo, car c'est facile. Cela fonctionne mais lorsque les données changent, la page se redessine évidemment. C'est mauvais alors j'essaie d'utiliser la voie d'Alvaro Luis Bustamante. C'est parfait. Voici le code:
Et quand les données changent:
la source
Juste au cas où quelqu'un utiliserait l' adaptateur basé sur FragmentStatePagerAdapter (qui permettra à ViewPager de créer les pages minimales nécessaires à l'affichage, au plus 2 pour mon cas), la réponse de @ rui.araujo d'écraser getItemPosition dans votre adaptateur ne causera pas de gaspillage important, mais cela continuera peut être amélioré.
En pseudo code:
la source
getItemPosition()
lorsque le jeu de données a changé, et ViewPager saura qu'ils sont modifiés ou non. malheureusement, ViewPager ne l'a pas fait.J'ai eu un problème similaire dans lequel j'avais quatre pages et l'une des pages a mis à jour les vues sur les trois autres. J'ai pu mettre à jour les widgets (SeekBars, TextViews, etc.) sur la page adjacente à la page actuelle. Les deux dernières pages auraient des widgets non initialisés lors de l'appel
mTabsAdapter.getItem(position)
.Pour résoudre mon problème, j'ai utilisé
setSelectedPage(index)
avant d'appelergetItem(position)
. Cela instancierait la page, me permettant de pouvoir modifier les valeurs et les widgets sur chaque page.Après toutes les mises à jour que j'utiliserais,
setSelectedPage(position)
suivies denotifyDataSetChanged()
.Vous pouvez voir un léger scintillement dans le ListView sur la page de mise à jour principale, mais rien de notable. Je ne l'ai pas testé complètement, mais cela résout mon problème immédiat.
la source
Je poste juste cette réponse au cas où quelqu'un d'autre la trouverait utile. Pour faire exactement la même chose, j'ai simplement pris le code source de ViewPager et PagerAdapter de la bibliothèque de compatibilité et je l'ai compilé dans mon code (vous devez trier toutes les erreurs et les importer vous-même, mais cela peut certainement être fait).
Ensuite, dans le CustomViewPager, créez une méthode appelée updateViewAt (int position). La vue elle-même peut être obtenue à partir des objets ArrayList définis dans la classe ViewPager (vous devez définir un identifiant pour les vues à l'élément instancier et comparer cet identifiant avec la position dans la méthode updateViewAt ()). Ensuite, vous pouvez mettre à jour la vue si nécessaire.
la source
Je suppose que j'ai la logique de ViewPager.
Si j'ai besoin de rafraîchir un ensemble de pages et de les afficher en fonction du nouvel ensemble de données, j'appelle notifyDataSetChanged () . Ensuite, ViewPager effectue un certain nombre d'appels à getItemPosition () , en y passant Fragment en tant qu'objet . Ce fragment peut provenir d'un ancien ensemble de données (que je souhaite supprimer) ou d'un nouveau (que je souhaite afficher). Donc, je remplace getItemPosition () et là je dois déterminer en quelque sorte si mon fragment provient de l'ancien ensemble de données ou du nouveau.
Dans mon cas, j'ai une disposition à 2 volets avec une liste des éléments supérieurs dans le volet gauche et une vue de balayage (ViewPager) à droite. Donc, je stocke un lien vers mon élément supérieur actuel dans mon PagerAdapter et également à l'intérieur de chaque fragment de page instancié. Lorsque l'élément supérieur sélectionné dans la liste change, je stocke le nouvel élément supérieur dans PagerAdapter et j'appelle notifyDataSetChanged () . Et dans la getItemPosition () substituée, je compare l'élément supérieur de mon adaptateur à l'élément supérieur de mon fragment. Et seulement si elles ne sont pas égales, je renvoie POSITION_NONE. Ensuite, PagerAdapter rétablit tous les fragments qui ont renvoyé POSITION_NONE.
REMARQUE. Le stockage de l'ID de l'élément supérieur au lieu d'une référence peut être une meilleure idée.
L'extrait de code ci-dessous est un peu schématique, mais je l'ai adapté du code qui fonctionne réellement.
Merci à tous les chercheurs précédents!
la source
Le code ci-dessous a fonctionné pour moi.
Créez une classe qui étend la classe FragmentPagerAdapter comme ci-dessous.
Ensuite, à l'intérieur de chaque fragment que vous avez créé, créez une méthode updateFragment. Dans cette méthode, vous modifiez les éléments à modifier dans le fragment. Par exemple, dans mon cas, Fragment0 contenait un GLSurfaceView qui affiche un objet 3D basé sur un chemin vers un fichier .ply, donc à l'intérieur de ma méthode updateFragment je change le chemin vers ce fichier ply.
puis créez une instance de ViewPager,
et une instance Adpater,
alors fais ça,
Ensuite, à l'intérieur de la classe, vous avez initialisé la classe Adapter ci-dessus et créé un viewPager, chaque fois que vous souhaitez mettre à jour l'un de vos fragments (dans notre cas, Fragment0), utilisez ce qui suit:
Cette solution était basée sur la technique suggérée par Alvaro Luis Bustamante.
la source
1.Tout d'abord, vous devez définir la méthode getItemposition dans votre classe Pageradapter 2.Vous devez lire la position exacte de votre View Pager 3.puis envoyer cette position comme emplacement de données de votre nouveau 4.Write update button onclick listener inside the setonPageChange auditeur
ce code de programme est un peu i modifié pour définir l'élément de position particulier uniquement
la source
ce qui a fonctionné pour moi allait
viewPager.getAdapter().notifyDataSetChanged();
et dans l'adaptateur mettant votre code pour mettre à jour la vue à l'intérieur
getItemPosition
comme sice n'est peut-être pas la façon la plus correcte de procéder, mais cela a fonctionné (l'
return POSITION_NONE
astuce a provoqué un crash pour moi, ce n'était donc pas une option)la source
Vous pouvez mettre à jour dynamiquement tous les fragments, vous pouvez le voir en trois étapes.
Dans votre adaptateur:
Maintenant dans votre activité:
Enfin dans votre fragment, quelque chose comme ça:
Vous pouvez voir le code complet ici .
Merci Alvaro Luis Bustamante.
la source
Toujours retourner
POSITION_NONE
est simple mais un peu inefficace car cela évoque l'instanciation de toutes les pages qui ont déjà instancié.J'ai créé une bibliothèque ArrayPagerAdapter pour changer dynamiquement des éléments dans PagerAdapters.
En interne, les adaptateurs de cette bibliothèque reviennent
POSITION_NONE
surgetItemPosiition()
seulement si nécessaire.Vous pouvez modifier des éléments dynamiquement comme le suivant en utilisant cette bibliothèque.
La bibliothèque Thils prend également en charge les pages créées par Fragments.
la source
C'est pour tous ceux comme moi, qui ont besoin de mettre à jour le Viewpager à partir d'un service (ou d'un autre thread d'arrière-plan) et aucune des propositions n'a fonctionné: Après un peu de vérification du journal, j'ai réalisé que la méthode notifyDataSetChanged () ne revient jamais. getItemPosition (objet Object) est appelé un tout se termine là sans traitement supplémentaire. Ensuite, j'ai trouvé dans les documents de la classe parent PagerAdapter (ne fait pas partie des documents des sous-classes), "Les modifications de l'ensemble de données doivent se produire sur le thread principal et doivent se terminer par un appel à notifyDataSetChanged ()". Ainsi, la solution de travail dans ce cas était (en utilisant FragmentStatePagerAdapter et getItemPosition (Object object) définies pour retourner POSITION_NONE):
puis l'appel à notifyDataSetChanged ():
la source
Vous pouvez ajouter une transformation de pageur sur Viewpager comme ceci
Dans le code ci-dessous, j'ai changé la couleur de ma vue lors de l'exécution lors du défilement du pager
la source
je sais que je suis dam tard mais ça peut aider quelqu'un. J'étends juste la réponse d'acceptation et j'ai également ajouté le commentaire là-dessus.
bien,
la réponse elle-même dit qu'elle est inefficace
donc afin de le faire se rafraîchir uniquement lorsque cela est nécessaire, vous pouvez le faire
la source
ViewPager n'a pas été conçu pour prendre en charge le changement de vue dynamique.
J'en ai eu la confirmation en recherchant un autre bug lié à celui-ci https://issuetracker.google.com/issues/36956111 et en particulier https://issuetracker.google.com/issues/36956111#comment56
Cette question est un peu ancienne, mais Google a récemment résolu ce problème avec ViewPager2 . Il permettra de remplacer les solutions faites à la main (non entretenues et potentiellement buggées) par des solutions standard. Cela empêche également de recréer inutilement des vues comme le font certaines réponses.
Pour des exemples de ViewPager2, vous pouvez vérifier https://github.com/googlesamples/android-viewpager2
Si vous souhaitez utiliser ViewPager2, vous devrez ajouter la dépendance suivante dans votre fichier build.gradle:
Ensuite, vous pouvez remplacer votre ViewPager dans votre fichier xml par:
Après cela, vous devrez remplacer ViewPager par ViewPager2 dans votre activité
ViewPager2 a besoin d'un RecyclerView.Adapter ou d'un FragmentStateAdapter, dans votre cas, il peut s'agir d'un RecyclerView.Adapter
Dans le cas où vous utilisiez un TabLayout, vous pouvez utiliser un TabLayoutMediator:
Vous pourrez ensuite actualiser vos vues en modifiant les données de votre adaptateur et en appelant la méthode notifyDataSetChanged
la source
Au lieu de retourner
POSITION_NONE
et de créer à nouveau tous les fragments, vous pouvez faire comme je l'ai suggéré ici: Mettre à jour ViewPager dynamiquement?la source
Je pense que j'ai fait un moyen simple de notifier les modifications de l'ensemble de données:
Tout d'abord, modifiez un peu le fonctionnement de la fonction instantiateItem:
pour "updateView", remplissez la vue avec toutes les données que vous souhaitez remplir (setText, setBitmapImage, ...).
vérifiez que destroyView fonctionne comme ceci:
Supposons maintenant que vous devez modifier les données, faites-le, puis appelez la fonction suivante sur le PagerAdapter:
Par exemple, si vous souhaitez informer toutes les vues affichées par le viewPager que quelque chose a changé, vous pouvez appeler:
C'est ça.
la source
Pour ce que ça vaut, sur KitKat + il semble que cela
adapter.notifyDataSetChanged()
suffit pour faire apparaître les nouvelles vues, à condition que vous ayezsetOffscreenPageLimit
suffisamment de hauteur. Je peux obtenir le comportement souhaité en faisantviewPager.setOffscreenPageLimit(2)
.la source
Je l' utilise en fait
notifyDataSetChanged()
surViewPager
etCirclePageIndicator
après que je l' appelledestroyDrawingCache()
surViewPager
et il fonctionne .. Aucun des autres solutions a fonctionné pour moi.la source