Ответы:
Основной ответ основан на имени, сгенерированном фреймворком. Если это когда-либо изменится, то это больше не будет работать.
Как насчет этого решения, переопределения 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);
}
}
Кажется, это работает для меня при работе с фрагментами, которые доступны. Фрагменты, которые еще не были созданы, при вызове возвращают ноль getRegisteredFragment
. Но я использовал это главным образом, чтобы получить ток Fragment
из ViewPager
: adapater.getRegisteredFragment(viewPager.getCurrentItem())
и это не вернется null
.
Я не знаю никаких других недостатков этого решения. Если таковые имеются, я хотел бы знать.
MyPagerAdapter
не registerdFragments
будет потеряно из-за жизненного цикла (то есть поворота) ? Придется ли сохранить файл Activity
/ Fragment
using , а затем обновить его с помощью новой ссылки? MyPagerAdapter
onSaveInstanceState
FragmentManager
getItem
не вызывается снова при повороте (для любых уже созданных фрагов), так как FragmentManager
восстанавливает состояния, которые Fragments
он держит в пейджере. Если instantiateItem
вызывается, когда каждый Fragment
восстанавливается, то это решение на самом деле является более безопасным и более надежным в будущем, чем мое или принятый ответ. Я подумаю попробовать это сам.
Для получения фрагментов из ViewPager есть много ответов здесь и в других связанных с этим темах / блогах. Все, кого я видел, сломаны, и обычно они попадают в один из двух типов, перечисленных ниже. Существуют и другие допустимые решения, если вы хотите получить только текущий фрагмент, как этот другой ответ в этой теме.
При использовании FragmentPagerAdapter
см. Ниже. Если использовать FragmentStatePagerAdapter
его стоит посмотреть на это . Захват индексов, которые не являются текущими в FragmentStateAdapter, не так полезен, так как по своей природе они будут полностью снесены, вне поля зрения / вне границ ScScreenLimit.
Неправильно: ведите свой собственный внутренний список фрагментов, добавленный к тому, когда FragmentPagerAdapter.getItem()
вызывается
SparseArray
илиMap
getItem
вызывается только в первый раз, когда страница прокручивается (или получается, если ViewPager.setOffscreenPageLimit(x)
> 0) в ViewPager
, если хостинг Activity
/ Fragment
убит или перезапущен, то внутреннее SpaseArray
будет уничтожено, когда пользовательский FragmentPagerActivity будет воссоздан, но за кулисами ViewPagers внутренние фрагменты будут воссозданы, и getItem
будет НЕ называться любой из индексов, поэтому возможность получить фрагмент из индекса будут потеряны навсегда. Вы можете объяснить это за счет экономии, и восстановление этих ссылок фрагментов с помощью FragmentManager.getFragment()
и , putFragment
но это начинает запутаться ИМХО.Неправильно. Создайте свой собственный идентификатор тега, соответствующий тому, что используется внутри,FragmentPagerAdapter
и используйте его для извлечения фрагментов страницы изFragmentManager
ViewPager
этому. изменить в любое время или для любой версии ОС.Метод, воссозданный для этого решения,
private static String makeFragmentName(int viewId, long id) {
return "android:switcher:" + viewId + ":" + id;
}
ViewPager.instantiateItem()
Подход, аналогичный getItem()
описанному выше, но не нарушающий жизненный цикл, заключается в том, чтобы подключиться к нему, instantiateItem()
а не так, getItem()
как первый будет вызываться при каждом создании / обращении к индексу. Смотрите этот ответ
FragmentViewPager
Создайте свой собственный FragmentViewPager
класс из источника последней библиотеки поддержки и измените метод, используемый внутри для генерации тегов фрагмента. Вы можете заменить его ниже. Преимущество этого в том, что вы знаете, что создание тега никогда не изменится, и вы не полагаетесь на частный API / метод, который всегда опасен.
/**
* @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
или подкласс), зная, что результат может быть нулевым, если 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;
}
Это простое решение, которое решает проблемы в двух других решениях, которые можно найти в Интернете.
instantiateItem()
когда вы хотите получить доступ к фрагменту, который еще не был посещен после того, как произошло событие жизненного цикла и было посещено до события жизненного цикла. Небольшая разница, которую я знаю, но она привела к незначительной ошибке в моей программе, поэтому я изучаю это.
getItemId(pos)
внутриFragmentStatePagerAdapter
Добавьте следующие методы в ваш 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 . Если что-то не получится, вы увидите хороший журнал аварий.
Еще одно простое решение:
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);
}
}
setPrimaryItem()
называется после того, как ViewPager.OnPageChangeListener#onPageSelected()
я не могу его использовать :-(
Я знаю, что у этого есть несколько ответов, но, возможно, это поможет кому-то. Я использовал относительно простое решение, когда мне нужно было получить 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)
!!! РЕДАКТИРОВАТЬ !!!
Я обнаружил, что он getItem
вызывается только тогда, FragmentPagerAdapter
когда каждый Fragment
должен быть создан в первый раз , после этого кажется, что Fragments
они перерабатываются с использованием FragmentManager
. Таким образом, многие реализации FragmentPagerAdapter
создают новые Fragment
с getItem
. Используя мой метод, описанный выше, это означает, что мы будем создавать новые Fragment
s каждый раз, когда 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;
}
ViewPager
Сам устанавливает эти теги. Это решение хрупкое, потому что оно основано на знании «скрытого» соглашения об именах ViewPager
. Ответ «Улицы Бостона» - гораздо более комплексное решение, которое я сейчас использую в своем проекте (вместо этого подхода).
Для моего случая ни одно из вышеперечисленных решений не сработало.
Однако, поскольку я использую дочерний менеджер фрагментов во фрагменте, было использовано следующее:
Fragment f = getChildFragmentManager().getFragments().get(viewPager.getCurrentItem());
Это будет работать только в том случае, если ваши фрагменты в диспетчере соответствуют элементу ViewPager.
Для того, чтобы получить текущий Видимый фрагмент из ViewPager . Я использую это простое утверждение, и оно работает нормально.
public Fragment getFragmentFromViewpager()
{
return ((Fragment) (mAdapter.instantiateItem(mViewPager, mViewPager.getCurrentItem())));
}
Я обработал это, сначала составив список всех фрагментов ( 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();
}
но это только мой подход к хаку и слэшу, но он сработал для меня, я использую его, чтобы отменить изменения в отображаемом фрагменте, когда нажата кнопка «Назад».
Это основано на ответе Стивена выше. Это вернет фактический экземпляр фрагмента, который уже присоединен к родительскому действию.
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
}
}
}
}
Я не мог найти простой, чистый способ сделать это. Однако виджет ViewPager - это просто еще одна ViewGroup, в которой хранятся ваши фрагменты. ViewPager имеет эти фрагменты как непосредственные потомки. Таким образом, вы можете просто перебрать их (используя .getChildCount () и .getChildAt ()) и посмотреть, загружен ли в данный момент искомый экземпляр фрагмента в ViewPager и получить ссылку на него. Например, вы можете использовать какое-то статическое поле уникального идентификатора, чтобы различать фрагменты.
Обратите внимание, что ViewPager, возможно, не загрузил искомый фрагмент, так как это виртуализирующий контейнер, такой как ListView.
FragmentPagerAdapter - это фабрика фрагментов. Чтобы найти фрагмент, основанный на его позиции, если он все еще находится в памяти, используйте это:
public Fragment findFragmentByPosition(int position) {
FragmentPagerAdapter fragmentPagerAdapter = getFragmentPagerAdapter();
return getSupportFragmentManager().findFragmentByTag(
"android:switcher:" + getViewPager().getId() + ":"
+ fragmentPagerAdapter.getItemId(position));
}
Пример кода для поддержки API v4.
Вам не нужно вызывать 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);
}
getItemPosition()
это метод в вашем адаптере. Вы не будете звонить прямо. У вас есть ссылка на ваш адаптер, поэтому вы звоните, adapter.notifyDataSetChanged()
что, в свою очередь, вызовет getItemPosition()
ваш адаптер, передавая ссылку вашего Fragment
s. Вы можете увидеть полную реализацию в действии здесь stackoverflow.com/questions/19891828/…
Fragment
но вопрос о том, как получить ссылку, спорен. Другие решения получить справку с FragmentManager
помощью строки аналогична "android:switcher:"+id
или возвращения POSITION_NONE
из getItemosition()
чего все фрагменты воссоздать себя.
Должен распространяться 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;
Эй, я ответил на этот вопрос здесь . В основном вам нужно переопределить
открытый объект instantiateItem (контейнер ViewGroup, позиция int)
метод FragmentStatePagerAdapter.
Лучшее решение - использовать расширение, которое мы создали в CodePath и которое называется SmartFragmentStatePagerAdapter . Следуя этому руководству, это значительно упрощает извлечение фрагментов и текущего выбранного фрагмента из ViewPager. Это также лучше справляется с управлением памятью фрагментов, встроенных в адаптер.
Самый простой и краткий способ. Если все ваши фрагменты ViewPager
относятся к разным классам, вы можете извлечь и различить их следующим образом:
public class MyActivity extends Activity
{
@Override
public void onAttachFragment(Fragment fragment) {
super.onAttachFragment(fragment);
if (fragment.getClass() == MyFragment.class) {
mMyFragment = (MyFragment) fragment;
}
}
}
Я реализовал это легко с немного другим подходом.
Мой пользовательский метод FragmentAdapter.getItem возвратил не новый MyFragment (), а экземпляр MyFragment, созданный в конструкторе FragmentAdapter.
Затем в своей деятельности я получил фрагмент из адаптера, проверил, является ли он экземпляром необходимого фрагмента, затем произвел приведение и использовал необходимые методы.
Создать целочисленный идентификатор ресурса в /values/integers.xml
<integer name="page1">1</integer>
<integer name="page2">2</integer>
<integer name="page3">3</integer>
Затем в функции getItem PagerAdapter:
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;
}
Простой способ перебирать фрагменты в диспетчере фрагментов. Найдите viewpager с аргументом позиции раздела, размещенный в общедоступном статическом 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;
}
Во фрагменте
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();
}
}
в TabLayout есть несколько вкладок для фрагмента. Вы можете найти фрагмент по тегу, используя индекс фрагмента.
Например индекс для Fragment1 равен 0, поэтому в findFragmentByTag()
методе передайте тег для Viewpager.after с помощью фрагмента транзакции, который вы можете добавить, замените фрагмент.
String tag = "android:switcher:" + R.id.viewPager + ":" + 0;
Fragment1 f = (Fragment1) getSupportFragmentManager().findFragmentByTag(tag);
Хорошо для адаптера 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;
}
}
Fragment yourFragment = yourviewpageradapter.getItem(int index);
index
это место фрагмента в адаптере, как вы добавили fragment1
вначале, так что получите fragment1
проход index
0 и так далее для отдыха
Fragment
в,WeakReference
чтобы гарантировать, что вы не предотвратите сборку уничтоженного фрагмента? Просто кажется, что это правильно ...