повторное использование фрагментов в фрагменте страницы - PullRequest
67 голосов
/ 08 августа 2011

У меня есть просмотрщик, который просматривает фрагменты.Мой подкласс 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 ]

100 голосов
/ 17 августа 2011

FragmentPagerAdapter уже кеширует Fragments для вас. Каждому фрагменту присваивается тег, а затем FragmentPagerAdapter пытается вызвать findFragmentByTag. Вызывается getItem только если результат от findFragmentByTag равен null. Так что вам не нужно самостоятельно кэшировать фрагменты.

67 голосов
/ 23 января 2012

Приложение к посту Джеффа:

Вы можете получить ссылку на ваш 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

23 голосов
/ 26 марта 2015

Кажется, многие люди, просматривающие этот вопрос, ищут способ сослаться на 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...
}
17 голосов
/ 18 июня 2013

Если фрагмент все еще находится в памяти, вы можете найти его с помощью этой функции.

public Fragment findFragmentByPosition(int position) {
    FragmentPagerAdapter fragmentPagerAdapter = getFragmentPagerAdapter();
    return getSupportFragmentManager().findFragmentByTag(
            "android:switcher:" + getViewPager().getId() + ":"
                    + fragmentPagerAdapter.getItemId(position));
}

Пример кода для API поддержки v4.

0 голосов
/ 02 мая 2015

Я знаю, что (теоретически) это не ответ на вопрос, а другой подход.

У меня была проблема, когда мне нужно было обновить видимые фрагменты. Что бы я ни пытался, потерпел неудачу и с треском провалился ...

Попробовав так много разных вещей, я наконец-то закончил, используя BroadCastReceiver. Просто отправьте трансляцию, когда вам нужно что-то сделать с видимыми фрагментами, и запишите ее во фрагмент.

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

ура

...