Podsumowanie problemu
Uwaga: w tej odpowiedzi zamierzam odwołać się do FragmentPagerAdapterkodu źródłowego. Ale ogólne rozwiązanie powinno również dotyczyć FragmentStatePagerAdapter.
Jeśli to czytasz, prawdopodobnie już wiesz, że FragmentPagerAdapter/ FragmentStatePagerAdapterma tworzyć Fragmentsdla 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 Activitypotrzebujesz odniesienia do nich, Fragmentsaby nad nimi pracować. Nie masz idani tagdla tych utworzonych, Fragmentsponieważ 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 Fragmentdzwonią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 tagstruktury, 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 Fragmentszwróconego przez FragmentPagerAdapterto elementu , który nie opiera się na wewnętrznym tagszestawie 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 tagszmiennymi składowymi / odwołaniami do klasy zamiast ze zmiennymi składowymi Fragments, możesz również pobrać tagszestaw FragmentPagerAdapterw ten sam sposób: UWAGA: nie dotyczy to, FragmentStatePagerAdapterponieważ nie jest ustawiane tagspodczas 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 tagzestawu przez FragmentPagerAdapteri zamiast tego używa odpowiednich interfejsów API do ich pobierania. W ten sposób, nawet jeśli tagzmiany w przyszłych wersjach SupportLibrarynadal będą bezpieczne.
Nie zapominaj, że w zależności od projektu Activity, nad Fragmentsktórym próbujesz pracować, może jeszcze istnieć lub nie, więc musisz to uwzględnić, nullsprawdzając przed użyciem referencji.
Ponadto, jeśli zamiast tego pracujesz FragmentStatePagerAdapter, nie chcesz przechowywać twardych odniesień do swoich, Fragmentsponieważ możesz ich mieć wiele, a twarde odniesienia niepotrzebnie zatrzymałyby je w pamięci. Zamiast tego zapisuj Fragmentodwołania w WeakReferencezmiennych 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...
}