Riepilogo del problema
Nota: in questa risposta farò riferimento FragmentPagerAdapter
e il suo codice sorgente. Ma la soluzione generale dovrebbe applicarsi anche a FragmentStatePagerAdapter
.
Se stai leggendo questo, probabilmente sai già che FragmentPagerAdapter
/ FragmentStatePagerAdapter
è pensato per creare Fragments
per te ViewPager
, ma durante la ricreazione dell'attività (sia da una rotazione del dispositivo o dal sistema che uccide la tua app per riguadagnare memoria) questi Fragments
non verranno creati di nuovo, ma invece il loro istanze recuperate daFragmentManager
. Ora dì che hai Activity
bisogno di ottenere un riferimento a questi Fragments
per lavorare su di loro. Non hai creato un id
o tag
per questi Fragments
perché FragmentPagerAdapter
impostali internamente . Quindi il problema è come ottenere un riferimento a loro senza queste informazioni ...
Problema con le soluzioni attuali: affidarsi al codice interno
Molte delle soluzioni che ho visto su questo e simili domande si basano su come ottenere un riferimento al esistente Fragment
chiamando FragmentManager.findFragmentByTag()
e imitando il tag creato internamente:"android:switcher:" + viewId + ":" + id
. Il problema con questo è che ti affidi al codice sorgente interno, che come tutti sappiamo non è garantito che rimanga lo stesso per sempre. Gli ingegneri Android di Google potrebbero facilmente decidere di modificare la tag
struttura che romperebbe il codice lasciandoti incapace di trovare un riferimento all'esistente Fragments
.
Soluzione alternativa senza fare affidamento sull'interno tag
Ecco un semplice esempio di come ottenere un riferimento al Fragments
restituito da FragmentPagerAdapter
che non si basa sull'insieme interno tags
su Fragments
. La chiave è sovrascrivere instantiateItem()
e salvare i riferimenti lì invece che in getItem()
.
public class SomeActivity extends Activity {
private FragmentA m1stFragment;
private FragmentB m2ndFragment;
// other code in your Activity...
private class CustomPagerAdapter extends FragmentPagerAdapter {
// other code in your custom FragmentPagerAdapter...
public CustomPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
// Do NOT try to save references to the Fragments in getItem(),
// because getItem() is not always called. If the Fragment
// was already created then it will be retrieved from the FragmentManger
// and not here (i.e. getItem() won't be called again).
switch (position) {
case 0:
return new FragmentA();
case 1:
return new FragmentB();
default:
// This should never happen. Always account for each position above
return null;
}
}
// Here we can finally safely save a reference to the created
// Fragment, no matter where it came from (either getItem() or
// FragmentManger). Simply save the returned Fragment from
// super.instantiateItem() into an appropriate reference depending
// on the ViewPager position.
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
// save the appropriate reference depending on position
switch (position) {
case 0:
m1stFragment = (FragmentA) createdFragment;
break;
case 1:
m2ndFragment = (FragmentB) createdFragment;
break;
}
return createdFragment;
}
}
public void someMethod() {
// do work on the referenced Fragments, but first check if they
// even exist yet, otherwise you'll get an NPE.
if (m1stFragment != null) {
// m1stFragment.doWork();
}
if (m2ndFragment != null) {
// m2ndFragment.doSomeWorkToo();
}
}
}
o se preferisci lavorare con tags
variabili / riferimenti al membro della classe invece Fragments
che puoi anche prendere l' tags
insieme da FragmentPagerAdapter
nello stesso modo: NOTA: questo non si applica a FragmentStatePagerAdapter
poiché non si imposta tags
durante la creazione del suo Fragments
.
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
// get the tags set by FragmentPagerAdapter
switch (position) {
case 0:
String firstTag = createdFragment.getTag();
break;
case 1:
String secondTag = createdFragment.getTag();
break;
}
// ... save the tags somewhere so you can reference them later
return createdFragment;
}
Si noti che questo metodo NON si basa sull'imitazione tag
dell'insieme interno di FragmentPagerAdapter
e utilizza invece API appropriate per il recupero. In questo modo anche se le tag
modifiche nelle future versioni di SupportLibrary
sarai comunque al sicuro.
Non dimenticare che, a seconda del design del tuo Activity
, quello su Fragments
cui stai cercando di lavorare può o non può ancora esistere, quindi devi tenerne conto facendo null
controlli prima di utilizzare i tuoi riferimenti.
Inoltre, se invece stai lavorando con FragmentStatePagerAdapter
, allora non vuoi mantenere riferimenti rigidi al tuo Fragments
perché potresti averne molti e riferimenti rigidi li terrebbero inutilmente in memoria. Salvare invece i Fragment
riferimenti in WeakReference
variabili anziché in quelli standard. Come questo:
WeakReference<Fragment> m1stFragment = new WeakReference<Fragment>(createdFragment);
// ...and access them like so
Fragment firstFragment = m1stFragment.get();
if (firstFragment != null) {
// reference hasn't been cleared yet; do work...
}