Короткий зміст проблеми
Примітка. У цій відповіді я посилаюся на посилання 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...
}