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.