Извлечение фрагмента из ViewPager

Я использую ViewPager вместе с FragmentStatePagerAdapter для размещения трех разных фрагментов:

  • [Fragment1]
  • [FRAGMENT2]
  • [Fragment3]

Когда я хочу получить Fragment1 из ViewPager в FragmentActivity.

В чем проблема и как его исправить?

Ответ 1

Основной ответ основывается на имени, генерируемом каркасом. Если это когда-либо изменится, то оно больше не будет работать.

Как насчет этого решения, переопределяя instantiateItem() и destroyItem() вашего Fragment(State)PagerAdapter:

public class MyPagerAdapter extends FragmentStatePagerAdapter {
    SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>();

    public MyPagerAdapter(FragmentManager fm) {
        super(fm);
    }

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

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

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment fragment = (Fragment) super.instantiateItem(container, position);
        registeredFragments.put(position, fragment);
        return fragment;
    }

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

    public Fragment getRegisteredFragment(int position) {
        return registeredFragments.get(position);
    }
}

Это, похоже, работает для меня, когда вы имеете дело с доступными фрагментами. Фрагменты, которые еще не были созданы, возвращают значение null при вызове getRegisteredFragment. Но я использовал это в основном для получения текущего Fragment из ViewPager: adapater.getRegisteredFragment(viewPager.getCurrentItem()), и это не вернет null.

Я не знаю никаких других недостатков этого решения. Если они есть, я хотел бы знать.

Ответ 2

Для захвата фрагментов из ViewPager есть много ответов здесь и на других связанных с ним потоках/блогах SO. Все, кого я видел, сломаны, и они, как правило, попадают в один из двух типов, перечисленных ниже. Существуют и другие допустимые решения, если вы хотите только захватить текущий фрагмент, например этот > другой ответ на этот поток.

При использовании FragmentPagerAdapter см. ниже. Если вы используете FragmentStatePagerAdapter, стоит посмотреть . Захватывающие индексы, которые не являются текущими в FragmentStateAdapter, не так полезны, как по своей природе, они будут полностью снесены из-за пределов обзора/вне пределов offScreenLimit.

НЕПРЕРЫВНЫЕ ПУТЫ

Неверно: сохраняйте свой собственный внутренний список фрагментов, добавленный к тому, когда FragmentPagerAdapter.getItem() называется

  • Обычно используется SparseArray или Map
  • Ни один из многих примеров, которые я видел, не учитывает события жизненного цикла, поэтому это решение является хрупким. Поскольку getItem вызывается только в первый раз, когда страница прокручивается (или получена, если ваша ViewPager.setOffscreenPageLimit(x) > 0) в ViewPager, если хостинг Activity/Fragment убит или перезапущен, тогда внутренняя SpaseArray будет уничтожен, когда пользовательская FragmentPagerActivity будет воссоздана, но за кулисами внутренние фрагменты ViewPagers будут воссозданы, а getItem будет вызываться NOT для любого из индексов, поэтому возможность получить фрагмент из индекса будет потерян навсегда. Вы можете объяснить это, сохранив и восстановив эти ссылки на фрагменты с помощью FragmentManager.getFragment() и putFragment, но это начинает запутываться IMHO.

Неверно: создайте собственный идентификатор тега, соответствующий тому, что используется под капотом в FragmentPagerAdapter, и используйте его для извлечения страницы Фрагменты из FragmentManager

  • Это лучше, поскольку он справляется с проблемой потери-фрагмента-ссылки в первом решении для внутренних массивов, но, как справедливо указано в ответах выше и в другом месте в сети, он чувствует себя взломанным, поскольку его private, внутренний для ViewPager, который может быть изменен в любое время или для любой версии ОС.

Метод, воссозданный для этого решения,

private static String makeFragmentName(int viewId, long id) {
    return "android:switcher:" + viewId + ":" + id;
}

HAPPY PATH: ViewPager.instantiateItem()

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

СЧАСТЛИВЫЙ ПУТЬ: создайте свой собственный FragmentViewPager

Создайте собственный FragmentViewPager класс из источника последней версии поддержки и измените метод, используемый для создания тегов фрагментов. Вы можете заменить его ниже. Это имеет то преимущество, что вы знаете, что создание тега никогда не изменится, и вы не полагаетесь на частный api/method, который всегда опасен.

/**
 * @param containerViewId the ViewPager this adapter is being supplied to
 * @param id pass in getItemId(position) as this is whats used internally in this class
 * @return the tag used for this pages fragment
 */
public static String makeFragmentName(int containerViewId, long id) {
    return "android:switcher:" + containerViewId + ":" + id;
}

Затем, как указывает документ, когда вы хотите захватить фрагмент, используемый для индекса, просто вызовите что-то вроде этого метода (который вы можете поместить в пользовательский FragmentPagerAdapter или подкласс), который знает результат может быть null, если getItem еще не был вызван для этой страницы, т.е. он еще не создан.

/**
 * @return may return null if the fragment has not been instantiated yet for that position - this depends on if the fragment has been viewed
 * yet OR is a sibling covered by {@link android.support.v4.view.ViewPager#setOffscreenPageLimit(int)}. Can use this to call methods on
 * the current positions fragment.
 */
public @Nullable Fragment getFragmentForPosition(int position)
{
    String tag = makeFragmentName(mViewPager.getId(), getItemId(position));
    Fragment fragment = getSupportFragmentManager().findFragmentByTag(tag);
    return fragment;
}

Это простое решение и решает проблемы в двух других решениях, найденных повсюду в Интернете.

Ответ 3

Добавьте следующие методы в свой FragmentPagerAdapter:

public Fragment getActiveFragment(ViewPager container, int position) {
String name = makeFragmentName(container.getId(), position);
return  mFragmentManager.findFragmentByTag(name);
}

private static String makeFragmentName(int viewId, int index) {
    return "android:switcher:" + viewId + ":" + index;
}

getActiveFragment (0) должен работать.

Вот решение, реализованное в ViewPager https://gist.github.com/jacek-marchwicki/d6320ba9a910c514424d. Если что-то не получится, вы увидите хороший журнал аварий.

Ответ 4

Еще одно простое решение:

    public class MyPagerAdapter extends FragmentPagerAdapter {
        private Fragment mCurrentFragment;

        public Fragment getCurrentFragment() {
            return mCurrentFragment;
        }
//...    
        @Override
        public void setPrimaryItem(ViewGroup container, int position, Object object) {
            if (getCurrentFragment() != object) {
                mCurrentFragment = ((Fragment) object);
            }
            super.setPrimaryItem(container, position, object);
        }
    }

Ответ 5

Я знаю, что у этого есть несколько ответов, но, возможно, это поможет кому-то. Я использовал относительно простое решение, когда мне нужно было получить Fragment из моего ViewPager. В Activity или Fragment, удерживая ViewPager, вы можете использовать этот код для циклического перехода через каждый Fragment, который он удерживает.

FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) mViewPager.getAdapter();
for(int i = 0; i < fragmentPagerAdapter.getCount(); i++) {
    Fragment viewPagerFragment = fragmentPagerAdapter.getItem(i);
    if(viewPagerFragment != null) {
        // Do something with your Fragment
        // Check viewPagerFragment.isResumed() if you intend on interacting with any views.
    }
}
  • Если вы знаете позицию своего Fragment в ViewPager, вы можете просто вызвать getItem(knownPosition).

  • Если вы не знаете позицию своего Fragment в ViewPager, вы можете иметь своих дочерних элементов Fragments реализовать интерфейс с помощью метода, например getUniqueId(), и использовать его для их дифференциации, Или вы можете пройти через все Fragments и проверить тип класса, например if(viewPagerFragment instanceof FragmentClassYouWant)

!!! EDIT!!!

Я обнаружил, что getItem только вызывается FragmentPagerAdapter, когда каждый Fragment должен быть создан первый раз, после этого появляется, что Fragments переработаны используя FragmentManager. Таким образом, многие реализации FragmentPagerAdapter создают новый Fragment в getItem. Используя мой метод выше, это означает, что мы будем создавать новый Fragment каждый раз, когда вызывается getItem, когда мы просматриваем все элементы в FragmentPagerAdapter. В связи с этим я нашел лучший подход, используя FragmentManager, чтобы получить каждый Fragment (используя принятый ответ). Это более полное решение и хорошо работает для меня.

FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) mViewPager.getAdapter();
for(int i = 0; i < fragmentPagerAdapter.getCount(); i++) {
    String name = makeFragmentName(mViewPager.getId(), i);
    Fragment viewPagerFragment = getChildFragmentManager().findFragmentByTag(name);
    // OR Fragment viewPagerFragment = getFragmentManager().findFragmentByTag(name);
    if(viewPagerFragment != null) {

        // Do something with your Fragment
        if (viewPagerFragment.isResumed()) {
            // Interact with any views/data that must be alive
        }
        else {
            // Flag something for update later, when this viewPagerFragment
            // returns to onResume
        }
    }
}

И вам понадобится этот метод.

private static String makeFragmentName(int viewId, int position) {
    return "android:switcher:" + viewId + ":" + position;
}

Ответ 6

В моем случае ни одно из вышеперечисленных решений не работало.

Однако, поскольку я использую Менеджер фрагментов для детей во Фрагменте, было использовано следующее:

Fragment f = getChildFragmentManager().getFragments().get(viewPager.getCurrentItem());

Это будет работать, только если ваши фрагменты в Менеджере соответствуют элементу viewpager.

Ответ 7

Чтобы получить текущий видимый фрагмент из ViewPager. Я использую этот простой оператор, и он отлично работает.

      public Fragment getFragmentFromViewpager()  
      {
         return ((Fragment) (mAdapter.instantiateItem(mViewPager, mViewPager.getCurrentItem())));
      }

Ответ 8

Это основано на ответе Стивена выше. Это вернет фактический экземпляр фрагмента, который уже привязан к родительской активности.

FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) mViewPager.getAdapter();
    for(int i = 0; i < fragmentPagerAdapter.getCount(); i++) {

        Fragment viewPagerFragment = (Fragment) mViewPager.getAdapter().instantiateItem(mViewPager, i);
        if(viewPagerFragment != null && viewPagerFragment.isAdded()) {

            if (viewPagerFragment instanceof FragmentOne){
                FragmentOne oneFragment = (FragmentOne) viewPagerFragment;
                if (oneFragment != null){
                    oneFragment.update(); // your custom method
                }
            } else if (viewPagerFragment instanceof FragmentTwo){
                FragmentTwo twoFragment = (FragmentTwo) viewPagerFragment;

                if (twoFragment != null){
                    twoFragment.update(); // your custom method
                }
            }
        }
    }

Ответ 9

Я не мог найти простой, чистый способ сделать это. Однако виджет ViewPager - это еще одна ViewGroup, в которой размещаются ваши фрагменты. ViewPager имеет эти фрагменты как непосредственные дети. Таким образом, вы можете просто перебрать их (используя .getChildCount() и .getChildAt()) и посмотреть, загружен ли экземпляр фрагмента, который вы ищете, в ViewPager и получить ссылку на него. Например. вы можете использовать некоторое статическое уникальное поле идентификатора, чтобы разделить фрагменты.

Обратите внимание, что ViewPager, возможно, не загрузил фрагмент, который вы ищете, поскольку он представляет собой виртуализующий контейнер, например ListView.

Ответ 10

FragmentPagerAdapter является factory фрагментов. Чтобы найти фрагмент, основанный на его позиции, если он все еще используется в памяти, используйте это:

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

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

Ответ 11

Я обработал его, сначала создав список всех фрагментов (List<Fragment> fragments;), которые я собирался использовать, а затем добавил их в пейджер, чтобы упростить обработку просматриваемого в данный момент фрагмента.

Итак:

@Override
onCreate(){
    //initialise the list of fragments
    fragments = new Vector<Fragment>();

    //fill up the list with out fragments
    fragments.add(Fragment.instantiate(this, MainFragment.class.getName()));
    fragments.add(Fragment.instantiate(this, MenuFragment.class.getName()));
    fragments.add(Fragment.instantiate(this, StoresFragment.class.getName()));
    fragments.add(Fragment.instantiate(this, AboutFragment.class.getName()));
    fragments.add(Fragment.instantiate(this, ContactFragment.class.getName()));


    //Set up the pager
    pager = (ViewPager)findViewById(R.id.pager);
    pager.setAdapter(new MyFragmentPagerAdapter(getSupportFragmentManager(), fragments));
    pager.setOffscreenPageLimit(4);
}

то это можно назвать:

public Fragment getFragment(ViewPager pager){   
    Fragment theFragment = fragments.get(pager.getCurrentItem());
    return theFragment;
}

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

Fragment tempFragment = getFragment();
if(tempFragment == MyFragmentNo2.class){
    MyFragmentNo2 theFrag = (MyFragmentNo2) tempFragment;
    //then you can do whatever with the fragment
    theFrag.costomFunction();
}

но это только мой подход к взлому и слэшу, но он работал у меня, я использую его, делают релевантные изменения для моего отображаемого в данный момент фрагмента при нажатии кнопки "Назад".

Ответ 12

Вам не нужно вызывать getItem() или какой-либо другой метод на более позднем этапе, чтобы получить ссылку Fragment, размещенную внутри ViewPager. Если вы хотите обновить некоторые данные внутри Fragment, используйте этот подход: Динамически обновить ViewPager?

Ключ должен установить новые данные внутри Adaper и вызвать notifyDataSetChanged(), который, в свою очередь, вызовет getItemPosition(), передав вам ссылку вашего Fragment и предоставив вам возможность обновить его. Все остальные способы требуют от вас ссылки на себя или на какой-либо другой хак, который не является хорошим решением.

@Override
public int getItemPosition(Object object) {
    if (object instanceof UpdateableFragment) {
        ((UpdateableFragment) object).update(xyzData);
    }
    //don't return POSITION_NONE, avoid fragment recreation. 
    return super.getItemPosition(object);
}

Ответ 13

Должен распространять FragmentPagerAdapter в ваш класс адаптера ViewPager.
Если вы используете FragmentStatePagerAdapter, то вы не сможете найти свой Fragment по ID

public static String makeFragmentName(int viewPagerId, int index) {
  return "android:switcher:" + viewPagerId + ":" + index;
}

Как использовать этот метод: -

Fragment mFragment = ((FragmentActivity) getContext()).getSupportFragmentManager().findFragmentByTag(
       AppMethodUtils.makeFragmentName(mViewPager.getId(), i)
);
InterestViewFragment newFragment = (InterestViewFragment) mFragment;

Ответ 14

Лучшим решением является использование расширения, которое мы создали в CodePath под названием SmartFragmentStatePagerAdapter. Следуя этому руководству, это значительно облегчает извлечение фрагментов и выделенного в данный момент фрагмента из ViewPager. Он также лучше справляется с управлением памятью фрагментов, встроенных в адаптер.

Ответ 15

Эй, я ответил на этот вопрос здесь. В принципе, вам нужно переопределить

public Object instantiateItem (контейнер ViewGroup, позиция int)

метод FragmentStatePagerAdapter.

Ответ 16

Самый простой и самый лаконичный путь. Если все ваши фрагменты в ViewPager имеют разные классы, вы можете получить их и отличить их от следующих:

public class MyActivity extends Activity
{

    @Override
    public void onAttachFragment(Fragment fragment) {
        super.onAttachFragment(fragment);
        if (fragment.getClass() == MyFragment.class) {
            mMyFragment = (MyFragment) fragment;
        }
    }

}

Ответ 17

Я реализовал это легко с помощью немного другого подхода.

Мой пользовательский метод FragmentAdapter.getItem возвратил не новый MyFragment(), а экземпляр MyFragment, созданный в конструкторе FragmentAdapter.

В моей работе я получил фрагмент из адаптера, проверьте, нужен ли ему экземпляр, необходимый для фрагментации, затем выполните листинг и используйте необходимые методы.

Ответ 18

Fragment yourFragment = yourviewpageradapter.getItem(int index);

index - это место фрагмента в адаптере, например, вы добавили fragment1 сначала, так что retreive fragment1 pass index как 0 и т.д. для отдыха

Ответ 19

Создать идентификатор целочисленного ресурса в файле /values/integers.xml

<integer name="page1">1</integer>
<integer name="page2">2</integer>
<integer name="page3">3</integer>

Затем в функции PagerAdapter getItem:

public Fragment getItem(int position) {

        Fragment fragment = null;

        if (position == 0) {
            fragment = FragmentOne.newInstance();
            mViewPager.setTag(R.integer.page1,fragment);

        }
        else if (position == 1) {

            fragment = FragmentTwo.newInstance();
            mViewPager.setTag(R.integer.page2,fragment);

        } else if (position == 2) {

            fragment = FragmentThree.newInstance();
            mViewPager.setTag(R.integer.page3,fragment);

        }

        return fragment;
        }

Затем в действии напишите эту функцию, чтобы получить ссылку на фрагмент:

private Fragment getFragmentByPosition(int position) {
    Fragment fragment = null;

    switch (position) {
        case 0:
            fragment = (Fragment) mViewPager.getTag(R.integer.page1);
            break;

        case 1:

            fragment = (Fragment) mViewPager.getTag(R.integer.page2);
            break;

        case 2:
            fragment = (Fragment) mViewPager.getTag(R.integer.page3);
            break;
            }

            return fragment;
    }

Получить ссылку на фрагмент, вызвав указанную выше функцию, а затем применить ее к своему пользовательскому фрагменту:

Fragment fragment = getFragmentByPosition(position);

        if (fragment != null) {
                    FragmentOne fragmentOne = (FragmentOne) fragment;
                    }

Ответ 20

Простой способ перебора фрагментов в менеджере фрагментов. Найти viewpager, имеющий аргумент позиции раздела, помещенный в открытый static PlaceholderFragment newInstance (int sectionNumber).

public PlaceholderFragment getFragmentByPosition(Integer pos){
    for(Fragment f:getChildFragmentManager().getFragments()){
        if(f.getId()==R.id.viewpager && f.getArguments().getInt("SECTNUM") - 1 == pos) {
            return (PlaceholderFragment) f;
        }
    }
    return null;
}

Ответ 21

В фрагменте

public int getArgument(){
   return mPage;
{
public void update(){

}

В FragmentActivity

List<Fragment> fragments = getSupportFragmentManager().getFragments();
for(Fragment f:fragments){
    if((f instanceof PageFragment)&&(!f.isDetached())){
          PageFragment pf = (PageFragment)f;   
          if(pf.getArgument()==pager.getCurrentItem())pf.update();
    }
}

Ответ 22

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

Напр. индекс для Fragment1 равен 0, поэтому в findFragmentByTag() передайте тег для Viewpager.after используя фрагментTransaction, который вы можете добавить, замените фрагмент.

String tag = "android:switcher:" + R.id.viewPager + ":" + 0; Fragment1 f = (Fragment1) getSupportFragmentManager().findFragmentByTag(tag);

Ответ 23

Хорошо для адаптера FragmentStatePagerAdapter Я финансирую решение:

в вашем FragmentActivity:

ActionBar mActionBar = getSupportActionBar(); 
mActionBar.addTab(mActionBar.newTab().setText("TAB1").setTabListener(this).setTag(Fragment.instantiate(this, MyFragment1.class.getName())));
mActionBar.addTab(mActionBar.newTab().setText("TAB2").setTabListener(this).setTag(Fragment.instantiate(this, MyFragment2.class.getName())));
mActionBar.addTab(mActionBar.newTab().setText("TAB3").setTabListener(this).setTag(Fragment.instantiate(this, MyFragment3.class.getName())));

viewPager = (STViewPager) super.findViewById(R.id.viewpager);
mPagerAdapter = new MyPagerAdapter(getSupportFragmentManager(), mActionBar);
viewPager.setAdapter(this.mPagerAdapter);

и создайте методу в вашем классе FragmentActivity. Таким образом, этот метод даст вам доступ к вашему фрагменту, вам просто нужно указать ему нужный фрагмент:

public Fragment getActiveFragment(int position) {
String name = MyPagerAdapter.makeFragmentName(position);
return getSupportFragmentManager().findFragmentByTag(name);
}

в вашем адаптере:

public class MyPagerAdapter extends FragmentStatePagerAdapter {

private final ActionBar actionBar;
private final FragmentManager fragmentManager;

public MyPagerAdapter(FragmentManager fragmentManager, com.actionbarsherlock.app.ActionBarActionBar mActionBar) {super(fragmentManager);
this.actionBar = mActionBar;
this.fragmentManager = fragmentManager;
}

@Override
public Fragment getItem(int position) {
getSupportFragmentManager().beginTransaction().add(mTchatDetailsFragment, makeFragmentName(position)).commit();
return (Fragment)this.actionBar.getTabAt(position);
}

@Override
public int getCount() {
return this.actionBar.getTabCount();
}

@Override
public CharSequence getPageTitle(int position) {
return this.actionBar.getTabAt(position).getText();
}

private static String makeFragmentName(int viewId, int index) {
return "android:fragment:" + index;
}

}

Ответ 24

Это легкое решение отлично работало для меня.

YourFragment fragment = (YourFragment)getSupportFragmentManager().getFragments().get(0);

(Если вы знаете позицию своего фрагмента в списке, то это решение будет работать)