Повторное использование фрагментов в 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. Просто отправьте трансляцию, когда вам нужно что-то сделать с видимыми фрагментами и захватить ее в фрагменте.

Если вам нужен какой-то ответ, вы также можете отправить его через трансляцию.

веселит