Краткое изложение проблемы
Примечание. В этом ответе я буду ссылаться на FragmentPagerAdapter
его исходный код. Но общее решение также должно применяться к FragmentStatePagerAdapter
.
Если вы читаете это, вы, вероятно, уже знаете, что FragmentPagerAdapter
/ FragmentStatePagerAdapter
предназначен для создания Fragments
для вас ViewPager
, но при воссоздании Activity (будь то вращение устройства или система, убивающая ваше приложение для восстановления памяти) они 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...
}