Відповіді:
Основна відповідь спирається на ім’я, що генерується рамкою. Якщо це колись зміниться, то воно більше не буде працювати.
Як щодо цього рішення, що передусім, 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
користувачі MyPagerAdapter
повинні зберегти його в onSaveInstanceState
, а потім потрібно оновити його новим FragmentManager
посиланням?
getItem
не повертається знову при повороті (для будь-яких фрагментів, які вже були створені), оскільки FragmentManager
відновлює стани цього вмісту Fragments
в пейджері. Якщо instantiateItem
виклик, коли кожне Fragment
відновлюється, то це рішення насправді є більш безпечним і майбутнім доказом, ніж моя чи прийнята відповідь. Я буду розглядати це сам.
Для вилучення фрагментів з ViewPager є багато відповідей тут і на інших пов'язаних темах / блогах SO. Усі, кого я бачив, поламані, і вони, як правило, потрапляють в один із двох перелічених нижче типів. Є деякі інші дійсні рішення, якщо ви хочете лише захопити поточний фрагмент, як цей інший відповідь у цій темі.
Якщо ви FragmentPagerAdapter
бачите нижче. Якщо ви користуєтеся, то FragmentStatePagerAdapter
варто на це подивитися . Захоплення індексів, які не є поточним у FragmentStateAdapter, не настільки корисне, оскільки за своєю природою вони будуть повністю зірвані вийшли з поля зору / виходу з-за меж екрана.
Неправильно: зберігайте власний внутрішній список фрагментів, доданих до 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;
}
Тоді, як каже doc, коли ви хочете захопити фрагмент, використаний для індексу, просто зателефонуйте на щось подібне до цього методу (який ви можете помістити у звичайний 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
s в 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());
Це працюватиме лише в тому випадку, якщо ваші фрагменти в диспетчері відповідають елементу перегляду сторінок.
Для того, щоб отримати поточний фрагмент Visible від 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));
}
Приклад коду для v4 підтримки api.
Вам не потрібно зателефонувати 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;
Гей , я відповів на це питання тут . В основному, вам потрібно перекрити
public Object 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>
Потім у функції 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;
}
Простий спосіб ітерації фрагментів у менеджері фрагментів. Знайдіть 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 є кілька вкладок для Fragment. ви можете знайти фрагмент за тегом, використовуючи індекс фрагмента.
Для екс. індекс для Fragment1 дорівнює 0, тому в findFragmentByTag()
методі передайте тег для Viewpager. Після цього за допомогою fragmentTransaction ви можете додати, замінити фрагмент.
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
щоб гарантувати , що ви не заважаєте зруйнований фрагмент зі збирача сміття? Просто здається, що це правильно зробити ...