J'ai une activité avec 3 fragments (A, B, C). Le fragment A consiste en une ViewPager
avec 2 ListFragments
. L'utilisateur peut taper sur un élément dans n'importe lequel des fragments de liste et, ce faisant, aller au fragment B.
Dans le fragment A je fais:
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
pagerAdapter = new PagerAdapter(getActivity().getSupportFragmentManager());
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragmentA, container, false);
vpPager = (ViewPager)view.findViewById(R.id.vpPager);
vpPager.setOffscreenPageLimit(2);
vpPager.setAdapter(pagerAdapter);
vpPager.addOnPageChangeListener(this);
return view;
}
Et la PagerAdapter
est la suivante:
private class PagerAdapter extends FragmentPagerAdapter {
private final ListFragment1 lf1 = ListFragment1 .newInstance();
private final ListFragment2 lf2 = ListFragment2 .newInstance();
public PagerAdapter(Android.support.v4.app.FragmentManager fm) {
super(fm);
}
@Override
public Android.support.v4.app.Fragment getItem(int position) {
switch (position) {
case 0: return lf1;
case 1: return lf2;
default: return null;
}
}
}
La première fois que l'activité est affichée, les fragments de la liste du visualiseur sont affichés correctement.
Les 2 fragments de viewpager chargent les données d'une base de données, et je ne le fais qu'une fois (lorsque les fragments sont créés).
L'utilisateur peut taper sur un élément et le fragment B est affiché. Si l'utilisateur appuie sur Retour, le fragment A est affiché. Cependant, les fragments de liste ne sont pas affichés (il en existe déjà une instance).
Se pourrait-il que la vue ait été détruite, même si des exemples existent?
Qu'est-ce qui ne va pas ici? Est-ce qu'il y a une meilleure approche?
MODIFIER
Si j'utilise newInstance
dans l'adaptateur de pageur, j'obtiens un IllegalStateException: not attached to activity
. En effet, je lance une tâche asynchrone comme suit:
@Override
public void onPageSelected(int position) {
Fragment fragment = pagerAdapter.getItem(position);
if (fragment instanceof IPagedFragment) {
((IPagedFragment) fragment).onShown();
}
}
Et onShown
est:
@Override
public void onShown() {
myTask= new MyTask();
myTask.execute((Void)null);
}
Quand puis-je démarrer la tâche pour pouvoir être sûr à 100% que le fragment est lié à l'activité et que la vue a été créée (je dois obtenir listview, etc. à partir de la présentation).
Vous devez utiliser ChildFragmentManager
comme ci-dessous.
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
pagerAdapter = new PagerAdapter(getChildFragmentManager()); //here used child fragment manager
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragmentA, container, false);
vpPager = (ViewPager)view.findViewById(R.id.vpPager);
vpPager.setOffscreenPageLimit(2);
vpPager.setAdapter(pagerAdapter);
vpPager.addOnPageChangeListener(this);
return view;
}
Cela fonctionne comme un charme dans mon code avec viewpager et fragment.
Tout à l'heure, je l'ai résolu après avoir lutté toute la journée, en utilisant getChildFragmentManager()
, Qui passe cela en tant que paramètre au pagerAdapter. et ça va marcher.
en utilisant pagerAdapter dans fragment, utilisez:
PagerAdapter adapter = new PagerAdapter(getChildFragmentManager());
et en cas d'activité, utilisez getFragmentManager ()
PagerAdapter adapter = new PagerAdapter(getFragmentManager());
Vous créez ListFragment1 et ListFragment2 à l'aide de Activity FragmentManager, alors que vous devez utiliser Fragment FragmentManager. Donc, modifiez le pagerAdapter = new PagerAdapter(getActivity().getSupportFragmentManager());
avec pagerAdapter = new PagerAdapter(getChildFragmentManager());
. De cette manière, les fragments du pager de vue seront «liés» au fragment hébergeant le viewpager. De plus, vous ne devez garder aucune référence aux fragments dans le viewpager: c'est quelque chose qu'Android gère déjà. Essayez avec:
private class PagerAdapter extends FragmentPagerAdapter {
public PagerAdapter(Android.support.v4.app.FragmentManager fm) {
super(fm);
}
@Override
public Android.support.v4.app.Fragment getItem(int position) {
switch (position) {
case 0: return ListFragment1.newInstance();
case 1: return ListFragment2.newInstance();
default: return null;
}
}
}
Au fait, vpPager.setOffscreenPageLimit(2);
est inutile car vous n’avez que 2 pages et c’est une méthode que je n’ai jamais utilisée, même si j’ai beaucoup de fragments à gérer, car elle nécessite de la mémoire.
À propos de votre mise à jour: supprimez toute logique liée à ViewPager traitant le fragment. Si vous devez démarrer une tâche asynchrone dans votre fragment, vous pouvez le faire en utilisant l’une des méthodes du cycle de vie des fragments: onResume (), onCreateView (), etc.
class IPagedFragment extends Fragment {
public void onResume() {
super.onResume();
myTask= new MyTask();
myTask.execute((Void)null);
}
}
et s'il vous plaît, supprimez le private final ListFragment1 lf1 = ListFragment1 .newInstance();
. Croyez-moi, ce n'est pas une bonne idée puisque vous avez une forte référence à vos fragments.
J'ai construit un projet simple que vous pouvez utiliser comme implémentation de référence. Vous pouvez télécharger le code source depuis ma dropbox .
utilisez getChildFragmentManager () au lieu de supportFragmentManager ()
Essayez de remplacer la méthode getItemPosition
dans votre FragmentPagerAdapter:
@Override
public int getItemPosition(Object object) {
return PagerAdapter.POSITION_NONE;
}
Si l'une des solutions ci-dessus ne fonctionne pas, vous pouvez essayer une solution de contournement en publiant (en différé) sur l'instance de la vue sur le pagineur un appel supplémentaire notifyDataSetChanged de l'adaptateur:
vpPager.post(new Runnable() {
@Override
public void run() {
pagerAdapter.notifyDataSetChanged();
}
});
ou
vpPager.postDelayed(new Runnable() {
@Override
public void run() {
pagerAdapter.notifyDataSetChanged();
}
}, 100 /* you have to find out the best delay time by trying/adjusting */);