Повторное использование фрагментов в фрагменте страницы
У меня есть просмотрщик, который просматривает фрагменты. мой 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));
}
}
5 ответов
FragmentPagerAdapter
уже кеширует Fragments
для тебя. Каждому фрагменту присваивается тег, а затем FragmentPagerAdapter
пытается позвонить findFragmentByTag
, Это только звонки getItem
если результат от findFragmentByTag
является null
, Так что вам не нужно самостоятельно кэшировать фрагменты.
Приложение к посту Джеффа:
Вы можете получить ссылку на ваш 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
Кажется, многие люди, просматривающие этот вопрос, ищут способ ссылаться на 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
так как это не устанавливает 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...
}
Если фрагмент все еще находится в памяти, вы можете найти его с помощью этой функции.
public Fragment findFragmentByPosition(int position) {
FragmentPagerAdapter fragmentPagerAdapter = getFragmentPagerAdapter();
return getSupportFragmentManager().findFragmentByTag(
"android:switcher:" + getViewPager().getId() + ":"
+ fragmentPagerAdapter.getItemId(position));
}
Пример кода для поддержки API v4.
Для будущих читателей!
Если вы думаете о повторном использовании фрагментов с помощью viewpager, лучшим решением будет использование ViewPager 2, поскольку View Pager 2 использует RecyclerView.
Я знаю, что это (теоретически) не ответ на вопрос, а другой подход.
У меня была проблема, когда мне нужно было обновить видимые фрагменты. Что бы я ни пытался, потерпел неудачу и потерпел неудачу...
Попробовав так много разных вещей, я наконец-то закончил, используя BroadCastReceiver
, Просто отправьте трансляцию, когда вам нужно что-то сделать с видимыми фрагментами, и запишите ее во фрагмент.
Если вам также нужен какой-то ответ, вы также можете отправить его через трансляцию.
ура