J'utilise une ViewPager
avec une FragmentStatePagerAdapter
pour héberger trois fragments différents:
Quand je veux obtenir Fragment1
de la ViewPager
dans la FragmentActivity
.
Quel est le problème et comment puis-je le résoudre?
La réponse principale repose sur un nom généré par le framework. Si cela change jamais, alors cela ne fonctionnera plus.
Qu'en est-il de cette solution, annulant les fonctions instantiateItem()
et destroyItem()
de votre Fragment(State)PagerAdapter
:
public class MyPagerAdapter extends FragmentStatePagerAdapter {
SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>();
public MyPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public int getCount() {
return ...;
}
@Override
public Fragment getItem(int position) {
return MyFragment.newInstance(...);
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = (Fragment) super.instantiateItem(container, position);
registeredFragments.put(position, fragment);
return fragment;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
registeredFragments.remove(position);
super.destroyItem(container, position, object);
}
public Fragment getRegisteredFragment(int position) {
return registeredFragments.get(position);
}
}
Cela semble fonctionner pour moi lorsqu'il s'agit de fragments disponibles. Les fragments qui n'ont pas encore été instanciés renverront null lors de l'appel de getRegisteredFragment
. Mais je l’utilise surtout pour obtenir la Fragment
actuelle de la ViewPager
: adapater.getRegisteredFragment(viewPager.getCurrentItem())
et cela ne renverra pas null
.
Je ne suis au courant d'aucun autre inconvénient de cette solution. S'il y en a, j'aimerais savoir.
Pour récupérer des fragments d’un ViewPager, il existe de nombreuses réponses ici et sur d’autres SO discussions/blogs connexes. Toutes les personnes que j'ai vues sont brisées et semblent généralement appartenir à l'un des deux types énumérés ci-dessous. Il existe d’autres solutions valables si vous voulez seulement récupérer le fragment actuel, comme this autre réponse sur ce fil de discussion.
Si vous utilisez FragmentPagerAdapter
, voir ci-dessous. Si vous utilisez FragmentStatePagerAdapter
, cela vaut la peine de regarder this . Saisir des index qui ne sont pas les index actuels dans FragmentStateAdapter n'est pas aussi utile que si elles étaient complètement détruites, elles étaient hors de vue/hors des limites offScreenLimit.} _
Wrong: conserve votre propre liste interne de fragments, ajoutée à l'appel de FragmentPagerAdapter.getItem()
SparseArray
ou Map
getItem
n'est appelé que la première fois qu'une page est défilée (ou obtenue si votre ViewPager.setOffscreenPageLimit(x)
> 0) dans la ViewPager
, si la Activity
Fragment
H&OCIRC;TE EST SUPPRIM&EACUTE;E OU RED&EACUTE;MARR&EACUTE;E, LA SpaseArray
INTERNE SERA EFFAC&EACUTE;E LORS DE LA RECR&EACUTE;ATION DE LA PROPRI&EACUTE;T&EACUTE; FRAGMENTPAGERACTIVITY , MAIS EN COULISSE, LES FRAGMENTS INTERNES DE VIEWPAGERS SERONT RECR&EACUTE;&EACUTE;S, ET getItem
SERA APPEL&EACUTE; _/_ ET NON pour aucun des index, de sorte que la possibilité d’obtenir un fragment à partir d’index sera définitivement perdue. Vous pouvez en tenir compte en sauvegardant et en restaurant ces références de fragments via FragmentManager.getFragment()
et putFragment
, mais cela commence à devenir désordonné à mon humble avis.Wrong: Construisez votre propre id de tag correspondant à ce qui est utilisé sous le capot dans FragmentPagerAdapter
et utilisez-le pour récupérer les fragments de page à partir de la FragmentManager
ViewPager
qui peut changer à tout moment ou pour n’importe quelle version du système d'exploitation.La méthode recréée pour cette solution est
private static String makeFragmentName(int viewId, long id) {
return "Android:switcher:" + viewId + ":" + id;
}
ViewPager.instantiateItem()
Une approche similaire à getItem()
ci-dessus mais sans rupture de cycle de vie consiste à y accrocher instantiateItem()
au lieu de getItem()
car le précédent sera appelé à chaque fois que cet index est créé/utilisé. Voir cette réponse
FragmentViewPager
Construisez votre propre classe FragmentViewPager
à partir de la source de la dernière version du support lib et modifiez la méthode utilisée en interne pour générer les balises fragment. Vous pouvez le remplacer par le suivant. Cela a pour avantage que vous savez que la création de balises ne changera jamais et que vous ne vous fiez pas à une méthode privée api /, ce qui est toujours dangereux.
/**
* @param containerViewId the ViewPager this adapter is being supplied to
* @param id pass in getItemId(position) as this is whats used internally in this class
* @return the tag used for this pages fragment
*/
public static String makeFragmentName(int containerViewId, long id) {
return "Android:switcher:" + containerViewId + ":" + id;
}
Ensuite, comme le dit la doc, lorsque vous souhaitez récupérer un fragment utilisé pour un index, appelez simplement cette méthode (que vous pouvez mettre dans la variable personnalisée FragmentPagerAdapter
ou une sous-classe), sachant que le résultat peut être null si getItem a pas encore été appelé pour cette page c'est-à-dire qu'il n'a pas encore été créé.
/**
* @return may return null if the fragment has not been instantiated yet for that position - this depends on if the fragment has been viewed
* yet OR is a sibling covered by {@link Android.support.v4.view.ViewPager#setOffscreenPageLimit(int)}. Can use this to call methods on
* the current positions fragment.
*/
public @Nullable Fragment getFragmentForPosition(int position)
{
String tag = makeFragmentName(mViewPager.getId(), getItemId(position));
Fragment fragment = getSupportFragmentManager().findFragmentByTag(tag);
return fragment;
}
C'est une solution simple qui résout les problèmes des deux autres solutions trouvées partout sur le Web.
Ajoutez les méthodes suivantes à votre FragmentPagerAdapter:
public Fragment getActiveFragment(ViewPager container, int position) {
String name = makeFragmentName(container.getId(), position);
return mFragmentManager.findFragmentByTag(name);
}
private static String makeFragmentName(int viewId, int index) {
return "Android:switcher:" + viewId + ":" + index;
}
getActiveFragment (0) doit fonctionner.
Voici la solution implémentée dans ViewPager https://Gist.github.com/jacek-marchwicki/d6320ba9a910c514424d . Si quelque chose échoue, vous verrez un bon journal des incidents.
Une autre solution simple:
public class MyPagerAdapter extends FragmentPagerAdapter {
private Fragment mCurrentFragment;
public Fragment getCurrentFragment() {
return mCurrentFragment;
}
//...
@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
if (getCurrentFragment() != object) {
mCurrentFragment = ((Fragment) object);
}
super.setPrimaryItem(container, position, object);
}
}
Je sais que cela a quelques réponses, mais peut-être que cela aidera quelqu'un. J'ai utilisé une solution relativement simple lorsque j'avais besoin d'obtenir un Fragment
de mon ViewPager
. Dans votre Activity
ou Fragment
tenant le ViewPager
, vous pouvez utiliser ce code pour parcourir chaque Fragment
qu'il détient.
FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) mViewPager.getAdapter();
for(int i = 0; i < fragmentPagerAdapter.getCount(); i++) {
Fragment viewPagerFragment = fragmentPagerAdapter.getItem(i);
if(viewPagerFragment != null) {
// Do something with your Fragment
// Check viewPagerFragment.isResumed() if you intend on interacting with any views.
}
}
Si vous connaissez la position de votre Fragment
dans ViewPager
, vous pouvez simplement appeler getItem(knownPosition)
.
Si vous ne connaissez pas la position de votre Fragment
dans ViewPager
, vous pouvez demander à vos enfants Fragments
d'implémenter une interface avec une méthode telle que getUniqueId()
et de l'utiliser pour les différencier. Vous pouvez également parcourir toutes les variables Fragments
et vérifier le type de classe, tel que if(viewPagerFragment instanceof FragmentClassYouWant)
.
!!! MODIFIER !!!
J'ai découvert que getItem
n'est appelé par un FragmentPagerAdapter
que lorsque chaque Fragment
doit être créé la première fois , après cela, il semble que les Fragments
sont recyclés à l'aide de FragmentManager
. De cette façon, de nombreuses implémentations de FragmentPagerAdapter
créent new Fragment
s dans getItem
. En utilisant ma méthode ci-dessus, cela signifie que nous allons créer un nouveau Fragment
s chaque fois que getItem
est appelé alors que nous parcourons tous les éléments de FragmentPagerAdapter
. Pour cette raison, j'ai trouvé une meilleure approche, en utilisant FragmentManager
pour obtenir chaque Fragment
à la place (en utilisant la réponse acceptée). C'est une solution plus complète et qui fonctionne bien pour moi.
FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) mViewPager.getAdapter();
for(int i = 0; i < fragmentPagerAdapter.getCount(); i++) {
String name = makeFragmentName(mViewPager.getId(), i);
Fragment viewPagerFragment = getChildFragmentManager().findFragmentByTag(name);
// OR Fragment viewPagerFragment = getFragmentManager().findFragmentByTag(name);
if(viewPagerFragment != null) {
// Do something with your Fragment
if (viewPagerFragment.isResumed()) {
// Interact with any views/data that must be alive
}
else {
// Flag something for update later, when this viewPagerFragment
// returns to onResume
}
}
}
Et vous aurez besoin de cette méthode.
private static String makeFragmentName(int viewId, int position) {
return "Android:switcher:" + viewId + ":" + position;
}
Pour obtenir le fragment visible actuel de ViewPager . J'utilise cette déclaration simple et ça fonctionne bien.
public Fragment getFragmentFromViewpager()
{
return ((Fragment) (mAdapter.instantiateItem(mViewPager, mViewPager.getCurrentItem())));
}
Ceci est basé sur la réponse de Steven ci-dessus. Cela retournera l'instance réelle du fragment qui est déjà attaché à l'activité parent.
FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) mViewPager.getAdapter();
for(int i = 0; i < fragmentPagerAdapter.getCount(); i++) {
Fragment viewPagerFragment = (Fragment) mViewPager.getAdapter().instantiateItem(mViewPager, i);
if(viewPagerFragment != null && viewPagerFragment.isAdded()) {
if (viewPagerFragment instanceof FragmentOne){
FragmentOne oneFragment = (FragmentOne) viewPagerFragment;
if (oneFragment != null){
oneFragment.update(); // your custom method
}
} else if (viewPagerFragment instanceof FragmentTwo){
FragmentTwo twoFragment = (FragmentTwo) viewPagerFragment;
if (twoFragment != null){
twoFragment.update(); // your custom method
}
}
}
}
Hey j'ai répondu à cette question ici . Fondamentalement, vous devez remplacer
public Object instantiateItem (conteneur ViewGroup, int position)
méthode de FragmentStatePagerAdapter.
Vous n'avez pas besoin d'appeler getItem()
ou une autre méthode à un stade ultérieur pour obtenir la référence d'une Fragment
hébergée dans ViewPager
. Si vous souhaitez mettre à jour des données dans Fragment
, utilisez cette approche: Mettre à jour ViewPager de manière dynamique?
La clé consiste à définir de nouvelles données dans Adaper
et à appeler notifyDataSetChanged()
, qui à son tour appelle getItemPosition()
, vous transmettant une référence de votre Fragment
et vous permettant de la mettre à jour. Tous les autres moyens exigent que vous gardiez une référence à vous-même ou à un autre hack qui n’est pas une bonne solution.
@Override
public int getItemPosition(Object object) {
if (object instanceof UpdateableFragment) {
((UpdateableFragment) object).update(xyzData);
}
//don't return POSITION_NONE, avoid fragment recreation.
return super.getItemPosition(object);
}
Je l'ai manipulé en faisant d'abord une liste de tous les fragments (List<Fragment> fragments;
) que j'allais utiliser, puis je les ai ajoutés au pageur, ce qui facilite la gestion du fragment actuellement visualisé.
Alors:
@Override
onCreate(){
//initialise the list of fragments
fragments = new Vector<Fragment>();
//fill up the list with out fragments
fragments.add(Fragment.instantiate(this, MainFragment.class.getName()));
fragments.add(Fragment.instantiate(this, MenuFragment.class.getName()));
fragments.add(Fragment.instantiate(this, StoresFragment.class.getName()));
fragments.add(Fragment.instantiate(this, AboutFragment.class.getName()));
fragments.add(Fragment.instantiate(this, ContactFragment.class.getName()));
//Set up the pager
pager = (ViewPager)findViewById(R.id.pager);
pager.setAdapter(new MyFragmentPagerAdapter(getSupportFragmentManager(), fragments));
pager.setOffscreenPageLimit(4);
}
alors cela peut être appelé:
public Fragment getFragment(ViewPager pager){
Fragment theFragment = fragments.get(pager.getCurrentItem());
return theFragment;
}
alors je pourrais jeter dans une déclaration si qui ne fonctionnerait que si elle était sur le bon fragment
Fragment tempFragment = getFragment();
if(tempFragment == MyFragmentNo2.class){
MyFragmentNo2 theFrag = (MyFragmentNo2) tempFragment;
//then you can do whatever with the fragment
theFrag.costomFunction();
}
mais c’est juste mon approche de hack et slash mais cela a fonctionné pour moi, je l’utilise pour effectuer des changements pertinents sur le fragment actuellement affiché lorsque le bouton de retour est enfoncé.
Je ne pouvais pas trouver un moyen simple et propre de faire cela. Cependant, le widget ViewPager n'est qu'un autre ViewGroup, qui héberge vos fragments. ViewPager a ces fragments comme enfants immédiats. Vous pouvez donc simplement les parcourir (en utilisant .getChildCount () et .getChildAt ()) et voir si l’instance de fragment que vous recherchez est actuellement chargée dans le ViewPager et y obtenir une référence. Par exemple. vous pouvez utiliser un champ d'identifiant unique statique pour distinguer les fragments.
Notez que le ViewPager n'a peut-être pas chargé le fragment recherché, car il s'agit d'un conteneur de virtualisation tel que ListView.
Doit étendre FragmentPagerAdapter
à votre classe d’adaptateur ViewPager.
Si vous utilisez FragmentStatePagerAdapter
, vous ne pourrez pas trouver votre Fragment
par son ID
public static String makeFragmentName(int viewPagerId, int index) {
return "Android:switcher:" + viewPagerId + ":" + index;
}
Comment utiliser cette méthode: -
Fragment mFragment = ((FragmentActivity) getContext()).getSupportFragmentManager().findFragmentByTag(
AppMethodUtils.makeFragmentName(mViewPager.getId(), i)
);
InterestViewFragment newFragment = (InterestViewFragment) mFragment;
FragmentPagerAdapter est la fabrique des fragments. Pour trouver un fragment basé sur sa position s'il est encore en mémoire, utilisez ceci:
public Fragment findFragmentByPosition(int position) {
FragmentPagerAdapter fragmentPagerAdapter = getFragmentPagerAdapter();
return getSupportFragmentManager().findFragmentByTag(
"Android:switcher:" + getViewPager().getId() + ":"
+ fragmentPagerAdapter.getItemId(position));
}
Exemple de code pour l'API de support v4.
La meilleure solution consiste à utiliser l’extension créée à l’emplacement CodePath appelée SmartFragmentStatePagerAdapter . Suivre ce guide facilite grandement la récupération des fragments et du fragment actuellement sélectionné à partir d'un ViewPager. Cela permet également de mieux gérer la mémoire des fragments incorporés dans l'adaptateur.
J'ai implémenté cela facilement avec une approche un peu différente.
Ma méthode personnalisée FragmentAdapter.getItem n'a pas renvoyé MyFragment (), mais l'instance de MyFragment créée dans le constructeur FragmentAdapter.
Dans mon activité, j'ai ensuite obtenu le fragment de l'adaptateur, vérifie s'il est instanceOf required Fragment, puis transforme et utilise les méthodes nécessaires.
dans TabLayout, il y a plusieurs onglets pour Fragment. vous pouvez trouver le fragment par Tag en utilisant l'index du fragment.
Par ex. L'index pour Fragment1 est 0. Par conséquent, dans la méthode findFragmentByTag()
, transmettez la balise pour le Viewpager. Après avoir utilisé fragmentTransaction, vous pouvez ajouter et remplacer le fragment.
String tag = "Android:switcher:" + R.id.viewPager + ":" + 0;
Fragment1 f = (Fragment1) getSupportFragmentManager().findFragmentByTag(tag);
Le moyen le plus simple et le plus concis. Si tous vos fragments dans ViewPager
appartiennent à des classes différentes, vous pouvez les récupérer et les distinguer comme suit:
public class MyActivity extends Activity
{
@Override
public void onAttachFragment(Fragment fragment) {
super.onAttachFragment(fragment);
if (fragment.getClass() == MyFragment.class) {
mMyFragment = (MyFragment) fragment;
}
}
}
Dans le fragment
public int getArgument(){
return mPage;
{
public void update(){
}
En FragmentActivité
List<Fragment> fragments = getSupportFragmentManager().getFragments();
for(Fragment f:fragments){
if((f instanceof PageFragment)&&(!f.isDetached())){
PageFragment pf = (PageFragment)f;
if(pf.getArgument()==pager.getCurrentItem())pf.update();
}
}
Créer un identifiant de ressource entier dans /values/integers.xml
<integer name="page1">1</integer>
<integer name="page2">2</integer>
<integer name="page3">3</integer>
Ensuite, dans la fonction getItem de PagerAdapter:
public Fragment getItem(int position) {
Fragment fragment = null;
if (position == 0) {
fragment = FragmentOne.newInstance();
mViewPager.setTag(R.integer.page1,fragment);
}
else if (position == 1) {
fragment = FragmentTwo.newInstance();
mViewPager.setTag(R.integer.page2,fragment);
} else if (position == 2) {
fragment = FragmentThree.newInstance();
mViewPager.setTag(R.integer.page3,fragment);
}
return fragment;
}
Ensuite, en activité, écrivez cette fonction pour obtenir la référence de fragment:
private Fragment getFragmentByPosition(int position) {
Fragment fragment = null;
switch (position) {
case 0:
fragment = (Fragment) mViewPager.getTag(R.integer.page1);
break;
case 1:
fragment = (Fragment) mViewPager.getTag(R.integer.page2);
break;
case 2:
fragment = (Fragment) mViewPager.getTag(R.integer.page3);
break;
}
return fragment;
}
Obtenez la référence au fragment en appelant la fonction ci-dessus, puis convertissez-la dans votre fragment personnalisé:
Fragment fragment = getFragmentByPosition(position);
if (fragment != null) {
FragmentOne fragmentOne = (FragmentOne) fragment;
}
Un moyen facile d'itérer des fragments dans le gestionnaire de fragments. Trouver le viewpager, qui a un argument de position de section, placé dans public statique PlaceholderFragment newInstance (int sectionNumber) .
public PlaceholderFragment getFragmentByPosition(Integer pos){
for(Fragment f:getChildFragmentManager().getFragments()){
if(f.getId()==R.id.viewpager && f.getArguments().getInt("SECTNUM") - 1 == pos) {
return (PlaceholderFragment) f;
}
}
return null;
}