Повторное использование фрагментов в фрагменте страницы

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

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

ура

Другие вопросы по тегам