Короткий зміст проблеми
Примітка. У цій відповіді я посилаюся на посилання FragmentPagerAdapterта його вихідний код. Але загальне рішення також має стосуватися і FragmentStatePagerAdapter.
Якщо ви читаєте це, ви, напевно, вже знаєте, що FragmentPagerAdapter/ FragmentStatePagerAdapterпризначено створити Fragmentsдля себе ViewPager, але після відтворення активності (чи від обертання пристрою чи від системи, що вбиває ваш додаток, щоб відновити пам'ять), вони Fragmentsне будуть створені знову, а замість них екземпляри, отримані зFragmentManager . Тепер скажіть, що вам Activityпотрібно отримати посилання на них, Fragmentsщоб виконати роботу над ними. У вас немає idабо tagдля цих створено, Fragmentsтому що FragmentPagerAdapter встановлюєте їх внутрішньо . Тому проблема полягає в тому, як отримати посилання на них без цієї інформації ...
Проблема з поточними рішеннями: покладаючись на внутрішній код
Дуже багато рішень, які я бачив на цьому та подібних питаннях, покладаються на отримання посилання на існуючий Fragment, зателефонувавши FragmentManager.findFragmentByTag()та імітуючи внутрішньо створений тег:"android:switcher:" + viewId + ":" + id . Проблема в цьому полягає в тому, що ви покладаєтесь на внутрішній вихідний код, який, як ми всі знаємо, не гарантовано залишиться таким же назавжди. Інженери Android від Google могли легко вирішити змінити tagструктуру, яка б порушила ваш код, не даючи змоги знайти посилання на існуючий Fragments.
Альтернативне рішення, не покладаючись на внутрішнє tag
Ось простий приклад того, як отримати посилання на Fragmentsповернене, FragmentPagerAdapterщо не покладається на внутрішній tagsнабір Fragments. Ключовим моментом є переосмислення instantiateItem()та збереження посилань там, а не в 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();
}
}
}
або якщо ви віддаєте перевагу працювати tagsзамість змінних / посилань члена класу на Fragmentsви можете також захопити tagsнабір FragmentPagerAdapterтаким же чином: ПРИМІТКА: це не стосується, FragmentStatePagerAdapterоскільки він не встановлюється tagsпри створенні його 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;
}
Зауважте, що цей метод НЕ покладається на наслідування внутрішнього tagнабору, FragmentPagerAdapterа замість цього використовує належні API для їх отримання. Таким чином, навіть якщо tagзміни в майбутніх версіях SupportLibraryви все одно будете в безпеці.
Не забувайте, що залежно від вашого дизайну Activity, Fragmentsви намагаєтеся працювати над тим, а може ще не існувати, тому вам доведеться це враховувати, роблячиnull перевірки, перш ніж використовувати свої посилання.
Крім того, якщо замість цього ви працюєте FragmentStatePagerAdapter, ви не хочете зберігати жорсткі посилання на свої, Fragmentsтому що у вас їх може бути багато, а жорсткі посилання зайво зберігатимуть їх у пам’яті. Замість цього збережіть Fragmentпосилання у WeakReferenceзмінних замість стандартних. Подобається це:
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...
}