Podsumowanie problemu
Uwaga: w tej odpowiedzi zamierzam odwołać się do FragmentPagerAdapter
kodu źródłowego. Ale ogólne rozwiązanie powinno również dotyczyć FragmentStatePagerAdapter
.
Jeśli to czytasz, prawdopodobnie już wiesz, że FragmentPagerAdapter
/ FragmentStatePagerAdapter
ma tworzyć Fragments
dla Ciebie ViewPager
, ale po odtworzeniu aktywności (czy to z obrotu urządzenia, czy systemu zabijającego aplikację w celu odzyskania pamięci) Fragments
, nie zostaną one ponownie utworzone, ale zamiast tego ich wystąpienia pobrane zFragmentManager
. Teraz powiedz, że Activity
potrzebujesz odniesienia do nich, Fragments
aby nad nimi pracować. Nie masz id
ani tag
dla tych utworzonych, Fragments
ponieważ FragmentPagerAdapter
ustawisz je wewnętrznie . Problem polega więc na tym, jak uzyskać do nich odniesienie bez tych informacji ...
Problem z obecnymi rozwiązaniami: poleganie na kodzie wewnętrznym
Wiele rozwiązań widziałem na tej i podobnych pytań polegać na coraz odniesienie do istniejącego Fragment
dzwoniąc FragmentManager.findFragmentByTag()
i imitując utworzony wewnętrznie tag:"android:switcher:" + viewId + ":" + id
. Problem polega na tym, że polegasz na wewnętrznym kodzie źródłowym, który, jak wszyscy wiemy, nie gwarantuje, że pozostanie taki sam na zawsze. Inżynierowie Androida w Google mogliby z łatwością zdecydować o zmianie tag
struktury, która zepsułaby Twój kod, uniemożliwiając znalezienie odniesienia do istniejącego Fragments
.
Alternatywne rozwiązanie bez polegania na wewnętrznym tag
Oto prosty przykład, jak uzyskać odwołanie do Fragments
zwróconego przez FragmentPagerAdapter
to elementu , który nie opiera się na wewnętrznym tags
zestawie Fragments
. Kluczem jest nadpisanie instantiateItem()
i zapisanie odwołań tam zamiast w 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();
}
}
}
lub jeśli wolisz pracować ze tags
zmiennymi składowymi / odwołaniami do klasy zamiast ze zmiennymi składowymi Fragments
, możesz również pobrać tags
zestaw FragmentPagerAdapter
w ten sam sposób: UWAGA: nie dotyczy to, FragmentStatePagerAdapter
ponieważ nie jest ustawiane tags
podczas tworzenia 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;
}
Zauważ, że ta metoda NIE polega na naśladowaniu wewnętrznego tag
zestawu przez FragmentPagerAdapter
i zamiast tego używa odpowiednich interfejsów API do ich pobierania. W ten sposób, nawet jeśli tag
zmiany w przyszłych wersjach SupportLibrary
nadal będą bezpieczne.
Nie zapominaj, że w zależności od projektu Activity
, nad Fragments
którym próbujesz pracować, może jeszcze istnieć lub nie, więc musisz to uwzględnić, null
sprawdzając przed użyciem referencji.
Ponadto, jeśli zamiast tego pracujesz FragmentStatePagerAdapter
, nie chcesz przechowywać twardych odniesień do swoich, Fragments
ponieważ możesz ich mieć wiele, a twarde odniesienia niepotrzebnie zatrzymałyby je w pamięci. Zamiast tego zapisuj Fragment
odwołania w WeakReference
zmiennych zamiast standardowych. Lubię to:
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...
}