Повторное использование фрагментов в fragmentpageradapter
У меня есть viewpager, что страницы через фрагменты. Мой подкласс FragmentPagerAdapter
создает новый фрагмент в методе getItem
, который кажется расточительным. Есть ли FragmentPagerAdapter
эквивалент convertView
в listAdapter
, который позволит мне повторно использовать уже созданные фрагменты? Мой код ниже.
public class ProfilePagerAdapter extends FragmentPagerAdapter {
ArrayList<Profile> mProfiles = new ArrayList<Profile>();
public ProfilePagerAdapter(FragmentManager fm) {
super(fm);
}
/**
* Adding a new profile object created a new page in the pager this adapter is set to.
* @param profile
*/
public void addProfile(Profile profile){
mProfiles.add(profile);
}
@Override
public int getCount() {
return mProfiles.size();
}
@Override
public Fragment getItem(int position) {
return new ProfileFragment(mProfiles.get(position));
}
}
Ответы
Ответ 1
FragmentPagerAdapter
уже кэширует Fragments
для вас. Каждому фрагменту присваивается тег, а затем FragmentPagerAdapter
пытается вызвать findFragmentByTag
. Он вызывает только getItem
, если результат из findFragmentByTag
равен null
. Поэтому вам не нужно кэшировать фрагменты самостоятельно.
Ответ 2
Приложение для записи Geoff:
Вы можете получить ссылку на Fragment
в FragmentPagerAdapter
с помощью findFragmentByTag()
. Имя тега генерируется следующим образом:
private static String makeFragmentName(int viewId, int index)
{
return "android:switcher:" + viewId + ":" + index;
}
где viewId является идентификатором ViewPager
Посмотрите на эту ссылку: http://code.google.com/p/openintents/source/browse/trunk/compatibility/AndroidSupportV2/src/android/support/v2/app/FragmentPagerAdapter.java#104
Ответ 3
Похоже, что многие люди, рассматривающие этот вопрос, ищут способ ссылки на Fragments
, созданный FragmentPagerAdapter
/FragmentStatePagerAdapter
. Я хотел бы предложить свое решение для этого, не полагаясь на внутренне созданную tags
, которую используют другие ответы здесь.
В качестве бонуса этот метод также должен работать с FragmentStatePagerAdapter
. Подробнее см. Примечания ниже.
Проблема с текущими решениями: опираясь на внутренний код
Многие решения, которые я видел на этом и подобные вопросы, полагаются на получение ссылки на существующий 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
, так как при создании Fragments
не устанавливается tags
.
@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...
}
Ответ 4
Если фрагмент все еще в памяти, вы можете найти его с помощью этой функции.
public Fragment findFragmentByPosition(int position) {
FragmentPagerAdapter fragmentPagerAdapter = getFragmentPagerAdapter();
return getSupportFragmentManager().findFragmentByTag(
"android:switcher:" + getViewPager().getId() + ":"
+ fragmentPagerAdapter.getItemId(position));
}
Пример кода для поддержки v4 api.
Ответ 5
Я знаю, что это (теоретически) не ответ на вопрос, а другой подход.
У меня возникла проблема, когда мне нужно было обновить видимые фрагменты. Независимо от того, что я пробовал, потерпел неудачу и потерпел неудачу...
Попробовав так много разных вещей, я, наконец, закончу это, используя BroadCastReceiver
. Просто отправьте трансляцию, когда вам нужно что-то сделать с видимыми фрагментами и захватить ее в фрагменте.
Если вам нужен какой-то ответ, вы также можете отправить его через трансляцию.
веселит
Ответ 6
Для будущих читателей!
Если вы думаете о повторном использовании фрагментов с помощью ViewPager, лучшим решением будет использование ViewPager 2, поскольку View Pager 2 использует RecyclerView.