Android FragmentStatePagerAdapter

Я работаю с FragmentStatePagerAdapter, используя этот пример.

Класс MyAdapter реализован следующим образом:

public static class MyAdapter extends FragmentStatePagerAdapter {
        public MyAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public int getCount() {
            return NUM_ITEMS;
        }

        @Override
        public Fragment getItem(int position) {
            return ArrayListFragment.newInstance(position);
        }
    }

Класс ListFragment включает следующий метод для создания нового экземпляра:

    /**
     * Create a new instance of CountingFragment, providing "num"
     * as an argument.
     */
    static ArrayListFragment newInstance(int num) {
        ArrayListFragment f = new ArrayListFragment();

        // Supply num input as an argument.
        Bundle args = new Bundle();
        args.putInt("num", num);
        f.setArguments(args);

        return f;
    }

Когда я создаю новый адаптер пейджера состояния в моей активности, вызывается getItem, который в свою очередь вызывает метод newInstance в классе ListFragment. Это здорово, когда я хочу создать новый фрагмент.

Но мне не ясно, как изменить getItem (если требуется), чтобы получить объект фрагмента, когда он уже существует, и пользовательские страницы, например, с страницы 2 на страницу 1. Мне нужна моя активность для извлечения существующего ранее созданного фрагмента, чтобы он мог запускать внутренний класс AsyncMethod, который находится в классе фрагмента.

Ответ 1

Я реализовал нечто похожее на то, что у вас есть. Я расширил класс FragmentPagerAdapter следующим образом:

public class ContactsFragmentPagerAdapter extends FragmentPagerAdapter {
    ActionBar mActionBar;
    private List<Fragment> mFragments;

    public ContactsFragmentPagerAdapter(FragmentManager fm, List<Fragment> fragments) {
        super(fm);
        mFragments = fragments;
    }

    @Override
    public int getCount() {
        return mFragments.size();
    }

    @Override
    public Fragment getItem(int position) {
        return mFragments.get(position);
    }

    public void setActionBar(ActionBar bar) {
        mActionBar = bar;
    }
}

Заметьте, что я добавил аргумент конструктору для передачи в List объектов Fragment. Таким образом, метод getItem() этого класса может возвращать любой класс, который расширяет Fragment или любой из его подклассов, а не только один конкретный класс ArrayListFragment, как вы это делали.

В Activity, где я создаю свой подкласс FragmentPagerAdapter, я прошел в списке объектов Fragment:

Класс создает экземпляр FragmentPagerAdapter

public final class ContactManager extends Activity {
    private ContactsFragmentPagerAdapter mAdapter;
    private ViewPager mPager;
    public ActionBar mActionBar;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.contact_manager);

        List<Fragment> fragments = new Vector<Fragment>();
        fragments.add(Fragment.instantiate(this, ContactsListFragment.class.getName()));
        fragments.add(Fragment.instantiate(this, GroupsListFragment.class.getName()));
        mAdapter = new ContactsFragmentPagerAdapter(this.getFragmentManager(), fragments);

        mPager = (ViewPager) findViewById(R.id.pager);
        mPager.setAdapter(mAdapter);

        mPager.setOnPageChangeListener(new OnPageChangeListener() {
            @Override
            public void onPageScrollStateChanged(int arg0) {}

            @Override
            public void onPageScrolled(int arg0, float arg1, int arg2) {}

            @Override
            public void onPageSelected(int arg0) {
                mActionBar.getTabAt(arg0).select();
            }
        });

    }

}

Получив доступ к переменной "фрагменты", вы можете получить доступ к ранее созданному Fragment, чтобы вы могли запускать методы этого Fragment.

Ответ 2

Я думаю, что решение намного проще, чем предоставленные ответы.

Если вы посмотрите на источник FragmentStatePagerAdapter.instantiateItem(), вы заметите, что instantiateItem() обрабатывает эту логику для вас, и, таким образом, ваша реализация getItem() должен всегда возвращать новый экземпляр.

Итак, чтобы вернуть существующий фрагмент, просто сделайте (если вы звоните из ViewPager):

Fragment f = (Fragment) getAdapter().instantiateItem(this, getCurrentItem());

Ответ 3

В то время как решение @imiric очень красноречиво, оно все равно беспокоит меня, что оно требует знания о реализации этого класса. Мое решение включает в себя добавление следующего к вашему адаптеру, которое должно вести себя хорошо с помощью FragmentStatePagerAdapter, возможно, уничтожая фрагменты:

    private SparseArray<WeakReference<Fragment>> mFragments = new SparseArray<>();

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment f = (Fragment) super.instantiateItem(container, position);
        mFragments.put(position, new WeakReference<>(f));  // Remember what fragment was in position
        return f;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        super.destroyItem(container, position, object);
        mFragments.remove(position);
    }

    public Fragment getFragment(int position) {
        WeakReference<Fragment> ref = mFragments.get(position);
        Fragment f = ref != null ? ref.get() : null;
        if (f == null) {
            Log.d(TAG, "fragment for " + position + " is null!");
        }
        return f;
    }

Ответ 4

public class MyPagerAdapter extends FragmentStatePagerAdapter {
    final Context m_context;
    final WeakReference<Fragment>[] m_fragments;

    public DetailPagerAdapter(FragmentManager fm) {
        super(fm);
        m_context = ...
                m_fragments = new WeakReference[enter size here];
    }

    @Override
    public Fragment getItem(int position) {
        final Fragment fragment = instantiate your fragment;
        m_fragments[position] = new WeakReference<Fragment>(fragment);
        return fragment;
    }

    @Override
    public int getCount() {
        return ...
    }

    public Fragment getFragment(final int position) {
        return m_fragments[position] == null ? null :
            m_fragments[position].get();
    }
}

Ответ 5

Не нужно изменять FragmentPagerAdapter, потому что фрагменты кэшируются FragmentManager. Поэтому вам нужно найти внутри него. Используйте эту функцию, чтобы найти фрагмент по позиции адаптера пейджера.

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

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