Réponses:
La réponse principale repose sur un nom généré par le framework. Si cela change, cela ne fonctionnera plus.
Qu'en est-il de cette solution, prioritaire 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 getRegisteredFragment
. Mais j'ai utilisé cela principalement pour extraire le courant Fragment
du ViewPager
: adapater.getRegisteredFragment(viewPager.getCurrentItem())
et cela ne reviendra pas null
.
Je ne connais aucun autre inconvénient de cette solution. S'il y en a, j'aimerais savoir.
MyPagerAdapter
est détruit en raison du cycle de vie (c'est-à-dire en rotation), ne sera pas registerdFragments
perdu? Est-ce que le Activity
/ Fragment
utilisant MyPagerAdapter
doivent enregistrer dans onSaveInstanceState
et alors besoin de le mettre à jour avec la nouvelle FragmentManager
ref?
getItem
ne sera pas appelé à nouveau lors de la rotation (pour les frags déjà créés), car le FragmentManager
restaure les états du Fragments
contenu dans le pager. Si instantiateItem
est appelé lorsque chacun Fragment
est restauré, cette solution est en fait plus sûre et plus pérenne que la mienne ou la réponse acceptée. J'envisagerai de l'essayer moi-même.
Pour récupérer des fragments d'un ViewPager, il y a beaucoup de réponses ici et sur d'autres threads / blogs SO connexes. Tous ceux que j'ai vus sont brisés et ils semblent généralement appartenir à l'un des deux types énumérés ci-dessous. Il existe d'autres solutions valides si vous souhaitez uniquement récupérer le fragment actuel, comme celui-ci autre réponse sur ce fil.
Si vous utilisez, FragmentPagerAdapter
voir ci-dessous. Si cela FragmentStatePagerAdapter
vaut la peine de regarder cela . Saisir des index qui ne sont pas ceux en cours dans un FragmentStateAdapter n'est pas aussi utile que par leur nature, ils seront complètement détruits hors de vue / hors des limites offScreenLimit.
Mauvais: conservez votre propre liste interne de fragments, ajoutée au moment de l' FragmentPagerAdapter.getItem()
appel
SparseArray
ouMap
getItem
n'est appelé que la première fois qu'une page est défilée vers (ou obtenue si votre ViewPager.setOffscreenPageLimit(x)
> 0) dans le ViewPager
, si l'hébergement Activity
/ Fragment
est tué ou redémarré, alors l'interne SpaseArray
sera effacé lorsque le FragmentPagerActivity personnalisé est recréé, mais en coulisses le ViewPagers fragments internes seront recréés et getItem
seront pas appelés pour l' un des indices, donc la possibilité d'obtenir un fragment de l' indice seront perdus à jamais. Vous pouvez en tenir compte en enregistrant et en restaurant ces références de fragments via FragmentManager.getFragment()
et putFragment
mais cela commence à devenir IMHO désordonné.Mauvais: créez votre propre identifiant de balise correspondant à ce qui est utilisé sous le capotFragmentPagerAdapter
et utilisez-le pour récupérer les fragments de page de laFragmentManager
ViewPager
cela pourrait changer à tout moment ou pour toute 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 à celle getItem()
ci-dessus mais sans rupture de cycle de vie consiste à s'y connecter au instantiateItem()
lieu de getItem()
car la première sera appelée à chaque fois que l'index est créé / accédé. Voir cette réponse
FragmentViewPager
Construisez votre propre FragmentViewPager
classe à partir de la source de la dernière bibliothèque de support et modifiez la méthode utilisée en interne pour générer les balises de fragment. Vous pouvez le remplacer par ce qui suit. Cela a l'avantage que vous savez que la création de balises ne changera jamais et que vous ne comptez pas sur une API / méthode privée, 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 le doc, lorsque vous voulez récupérer un fragment utilisé pour un index, appelez simplement quelque chose comme cette méthode (que vous pouvez mettre dans la coutume FragmentPagerAdapter
ou une sous-classe) sachant que le résultat peut être nul si getItem n'a pas encore été appelé pour cette page c'est-à-dire qu'elle n'a pas encore été créée.
/**
* @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;
}
Il s'agit d'une solution simple qui résout les problèmes des deux autres solutions trouvées partout sur le Web
instantiateItem()
lorsque vous souhaitez accéder à un fragment qui n'a pas encore été visité après qu'un événement de cycle de vie a eu lieu et a été visité avant l'événement de cycle de vie. Une petite différence que je connais mais c'est celle qui a conduit à un bug subtil dans mon programme, donc je me suis penché sur cela.
getItemId(pos)
intérieurFragmentStatePagerAdapter
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 plantages.
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);
}
}
setPrimaryItem()
on l'appelle après ViewPager.OnPageChangeListener#onPageSelected()
je ne peux pas l'utiliser :-(
Je sais que cela a quelques réponses, mais peut-être que cela aidera quelqu'un. Je l' ai utilisé une solution relativement simple quand je avais besoin pour obtenir un Fragment
de mes ViewPager
. Dans votre Activity
ou en Fragment
maintenant le ViewPager
, vous pouvez utiliser ce code pour parcourir chaque Fragment
contenu.
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 le ViewPager
, vous pouvez simplement appeler getItem(knownPosition)
.
Si vous ne connaissez pas la position de votre Fragment
dans le ViewPager
, vous pouvez demander à vos enfants d' Fragments
implémenter une interface avec une méthode comme getUniqueId()
, et de l'utiliser pour les différencier. Ou vous pouvez parcourir tout Fragments
et vérifier le type de classe, tel queif(viewPagerFragment instanceof FragmentClassYouWant)
!!! ÉDITER !!!
J'ai découvert que getItem
seulement est appelé par un FragmentPagerAdapter
lorsque chacun Fragment
doit être créé la première fois , après cela, il semble que le Fragments
sont recyclés à l'aide du FragmentManager
. De cette façon, de nombreuses implémentations de FragmentPagerAdapter
create new Fragment
s in getItem
. En utilisant ma méthode ci-dessus, cela signifie que nous créerons de nouveaux Fragment
s à chaque getItem
appel lorsque nous parcourons tous les éléments du FragmentPagerAdapter
. Pour cette raison, j'ai trouvé une meilleure approche, en utilisant le FragmentManager
pour obtenir chacun à la Fragment
place (en utilisant la réponse acceptée). Il s'agit d'une solution plus complète 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;
}
ViewPager
lui-même définit ces balises. Cette solution est fragile car elle repose sur la connaissance de la convention de nommage "cachée" du ViewPager
. La réponse de Streets of Boston est une solution beaucoup plus complète, que j'utilise maintenant dans mon projet (au lieu de cette approche).
Pour mon cas, aucune des solutions ci-dessus n'a fonctionné.
Cependant, puisque j'utilise le gestionnaire de fragments d'enfant dans un fragment, les éléments suivants ont été utilisés:
Fragment f = getChildFragmentManager().getFragments().get(viewPager.getCurrentItem());
Cela ne fonctionnera que si vos fragments dans le gestionnaire correspondent à l'élément du viseur.
Afin d'obtenir le fragment visible actuel de ViewPager . J'utilise cette simple déclaration et cela fonctionne bien.
public Fragment getFragmentFromViewpager()
{
return ((Fragment) (mAdapter.instantiateItem(mViewPager, mViewPager.getCurrentItem())));
}
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 pager, ce qui facilite la gestion du fragment actuellement affiché.
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 le jeter dans une instruction if 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 apporter des modifications pertinentes à mon fragment actuellement affiché lorsque le bouton de retour est enfoncé.
Ceci est basé sur la réponse de Steven ci-dessus. Cela retournera une 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
}
}
}
}
Je ne pouvais pas trouver un moyen simple et propre de le faire. Cependant, le widget ViewPager n'est qu'un autre ViewGroup, qui héberge vos fragments. Le ViewPager a ces fragments en tant qu'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 obtenir une référence. Par exemple, vous pouvez utiliser un champ ID unique statique pour distinguer les fragments.
Notez que le ViewPager n'a peut-être pas chargé le fragment que vous recherchez car il s'agit d'un conteneur de virtualisation comme ListView.
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.
Vous n'avez pas besoin d'appeler getItem()
ou d'une autre méthode à un stade ultérieur pour obtenir la référence d'un Fragment
hébergé à l'intérieur ViewPager
. Si vous souhaitez mettre à jour certaines données à l'intérieur, Fragment
utilisez cette approche: Mettre à jour ViewPager dynamiquement?
La clé est de définir de nouvelles données à l'intérieur Adaper
et d'appeler notifyDataSetChanged()
qui à leur tour appellera getItemPosition()
, vous transmettant une référence Fragment
et vous donnant la possibilité de la mettre à jour. Toutes les autres façons vous obligent à garder une référence à vous-même ou à un autre piratage 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);
}
getItemPosition()
est une méthode dans votre adaptateur. Vous ne l'appellerez pas directement. Vous avez une référence de votre adaptateur donc vous appelez adapter.notifyDataSetChanged()
qui à son tour appellera getItemPosition()
de votre adaptateur en passant la référence de votre Fragment
s. Vous pouvez voir une implémentation complète en action ici stackoverflow.com/questions/19891828/…
Fragment
mais comment obtenir une référence est discutable. D'autres solutions obtiennent des références en FragmentManager
utilisant une chaîne similaire à "android:switcher:"+id
ou en revenant POSITION_NONE
de getItemosition()
provoquer la recréation de tous les fragments.
Doit s'étendre FragmentPagerAdapter
dans votre classe d'adaptateur ViewPager.
Si vous utilisez FragmentStatePagerAdapter
alors vous ne pourrez pas trouver votre Fragment
par sonID
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;
Hé, j'ai répondu à cette question ici . Fondamentalement, vous devez remplacer
public Object instantiateItem (conteneur ViewGroup, position int)
méthode de FragmentStatePagerAdapter.
La meilleure solution consiste à utiliser l'extension que nous avons créée sur CodePath, appelée SmartFragmentStatePagerAdapter . En suivant ce guide, cela facilite considérablement la récupération des fragments et du fragment actuellement sélectionné à partir d'un ViewPager. Il gère également mieux la mémoire des fragments intégrés à l'adaptateur.
La manière la plus simple et la plus concise. Si tous vos fragments ViewPager
sont de 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;
}
}
}
J'ai implémenté cela facilement avec une approche un peu différente.
Ma méthode FragmentAdapter.getItem personnalisée 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érifiez s'il s'agit de l'instance du fragment nécessaire, puis converti et utilisez les méthodes nécessaires.
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, dans l'activité, écrivez cette fonction pour obtenir la référence du 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 du fragment en appelant la fonction ci-dessus, puis convertissez-la en votre fragment personnalisé:
Fragment fragment = getFragmentByPosition(position);
if (fragment != null) {
FragmentOne fragmentOne = (FragmentOne) fragment;
}
Un moyen facile d'itérer sur des fragments dans le gestionnaire de fragments. Trouvez le viewpager, qui a l'argument position de section, placé dans public PlaceholderFragment statique 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;
}
En fragment
public int getArgument(){
return mPage;
{
public void update(){
}
In FragmentActivity
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();
}
}
dans TabLayout, il existe plusieurs onglets pour Fragment. vous pouvez trouver le fragment par Tag en utilisant l'index du fragment.
Par exemple. l'index pour Fragment1 est 0, donc dans la findFragmentByTag()
méthode, passez la balise pour le Viewpager.Après avoir utilisé fragmentTransaction, vous pouvez ajouter, remplacer le fragment.
String tag = "android:switcher:" + R.id.viewPager + ":" + 0;
Fragment1 f = (Fragment1) getSupportFragmentManager().findFragmentByTag(tag);
Ok pour l'adaptateur FragmentStatePagerAdapter
je finance une solution:
dans votre FragmentActivité:
ActionBar mActionBar = getSupportActionBar();
mActionBar.addTab(mActionBar.newTab().setText("TAB1").setTabListener(this).setTag(Fragment.instantiate(this, MyFragment1.class.getName())));
mActionBar.addTab(mActionBar.newTab().setText("TAB2").setTabListener(this).setTag(Fragment.instantiate(this, MyFragment2.class.getName())));
mActionBar.addTab(mActionBar.newTab().setText("TAB3").setTabListener(this).setTag(Fragment.instantiate(this, MyFragment3.class.getName())));
viewPager = (STViewPager) super.findViewById(R.id.viewpager);
mPagerAdapter = new MyPagerAdapter(getSupportFragmentManager(), mActionBar);
viewPager.setAdapter(this.mPagerAdapter);
et créez une méthode dans votre classe FragmentActivity - Pour que cette méthode vous donne accès à votre Fragment, il vous suffit de lui donner la position du fragment que vous voulez:
public Fragment getActiveFragment(int position) {
String name = MyPagerAdapter.makeFragmentName(position);
return getSupportFragmentManager().findFragmentByTag(name);
}
dans votre adaptateur:
public class MyPagerAdapter extends FragmentStatePagerAdapter {
private final ActionBar actionBar;
private final FragmentManager fragmentManager;
public MyPagerAdapter(FragmentManager fragmentManager, com.actionbarsherlock.app.ActionBarActionBar mActionBar) {super(fragmentManager);
this.actionBar = mActionBar;
this.fragmentManager = fragmentManager;
}
@Override
public Fragment getItem(int position) {
getSupportFragmentManager().beginTransaction().add(mTchatDetailsFragment, makeFragmentName(position)).commit();
return (Fragment)this.actionBar.getTabAt(position);
}
@Override
public int getCount() {
return this.actionBar.getTabCount();
}
@Override
public CharSequence getPageTitle(int position) {
return this.actionBar.getTabAt(position).getText();
}
private static String makeFragmentName(int viewId, int index) {
return "android:fragment:" + index;
}
}
Fragment yourFragment = yourviewpageradapter.getItem(int index);
index
est la place du fragment dans l'adaptateur comme vous l'avez ajouté en fragment1
premier afin de récupérer le fragment1
passage index
à 0 et ainsi de suite pour le repos
Fragment
dansWeakReference
de vous garantir ne pas empêcher un fragment d'être détruit ordures collectées? Semble juste la bonne chose à faire ...