Я пытаюсь использовать фрагмент с ViewPager
использованием FragmentPagerAdapter
. Чего я хочу добиться, так это заменить фрагмент, расположенный на первой странице ViewPager
, другим.
Пейджер состоит из двух страниц. Первый - FirstPagerFragment
второй SecondPagerFragment
. Нажав на кнопку первой страницы. Я хотел бы заменить FirstPagerFragment
на NextFragment.
Ниже приведен мой код.
public class FragmentPagerActivity extends FragmentActivity {
static final int NUM_ITEMS = 2;
MyAdapter mAdapter;
ViewPager mPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_pager);
mAdapter = new MyAdapter(getSupportFragmentManager());
mPager = (ViewPager) findViewById(R.id.pager);
mPager.setAdapter(mAdapter);
}
/**
* Pager Adapter
*/
public static class MyAdapter extends FragmentPagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
@Override
public int getCount() {
return NUM_ITEMS;
}
@Override
public Fragment getItem(int position) {
if(position == 0) {
return FirstPageFragment.newInstance();
} else {
return SecondPageFragment.newInstance();
}
}
}
/**
* Second Page FRAGMENT
*/
public static class SecondPageFragment extends Fragment {
public static SecondPageFragment newInstance() {
SecondPageFragment f = new SecondPageFragment();
return f;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//Log.d("DEBUG", "onCreateView");
return inflater.inflate(R.layout.second, container, false);
}
}
/**
* FIRST PAGE FRAGMENT
*/
public static class FirstPageFragment extends Fragment {
Button button;
public static FirstPageFragment newInstance() {
FirstPageFragment f = new FirstPageFragment();
return f;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//Log.d("DEBUG", "onCreateView");
View root = inflater.inflate(R.layout.first, container, false);
button = (Button) root.findViewById(R.id.button);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
FragmentTransaction trans = getFragmentManager().beginTransaction();
trans.replace(R.id.first_fragment_root_id, NextFragment.newInstance());
trans.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
trans.addToBackStack(null);
trans.commit();
}
});
return root;
}
/**
* Next Page FRAGMENT in the First Page
*/
public static class NextFragment extends Fragment {
public static NextFragment newInstance() {
NextFragment f = new NextFragment();
return f;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//Log.d("DEBUG", "onCreateView");
return inflater.inflate(R.layout.next, container, false);
}
}
}
... а здесь XML-файлы
fragment_pager.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:padding="4dip"
android:gravity="center_horizontal"
android:layout_width="match_parent" android:layout_height="match_parent">
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1">
</android.support.v4.view.ViewPager>
</LinearLayout>
first.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/first_fragment_root_id"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button android:id="@+id/button"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="to next"/>
</LinearLayout>
Теперь проблема ... какой идентификатор я должен использовать в
trans.replace(R.id.first_fragment_root_id, NextFragment.newInstance());
?
Если я использую R.id.first_fragment_root_id
, замена работает, но Hierarchy Viewer показывает странное поведение, как показано ниже.
В начале ситуация
после замены ситуация
Как вы видите, что-то не так, я ожидаю найти то же состояние, что и на первом рисунке, после замены фрагмента.
Ответы:
Есть еще одно решение, которое не требует модификации исходного кода
ViewPager
иFragmentStatePagerAdapter
, и оно работает сFragmentPagerAdapter
базовым классом, используемым автором.Я хотел бы начать с ответа на вопрос автора о том, какой ID он должен использовать; это идентификатор контейнера, т.е. идентификатор самого пейджер просмотра. Однако, как вы, вероятно, заметили, использование этого идентификатора в вашем коде ничего не даст. Я объясню почему:
Прежде всего, чтобы сделать
ViewPager
повторное заполнение страниц, вам нужно вызватьnotifyDataSetChanged()
тот, который находится в базовом классе вашего адаптера.Во-вторых,
ViewPager
используетgetItemPosition()
абстрактный метод, чтобы проверить, какие страницы должны быть уничтожены, а какие должны быть сохранены. Реализация по умолчанию этой функции всегда возвращаетPOSITION_UNCHANGED
, что приводитViewPager
к сохранению всех текущих страниц и, следовательно, не присоединяет вашу новую страницу. Таким образом, чтобы замена фрагмента работала, ееgetItemPosition()
необходимо переопределить в адаптере и вернутьPOSITION_NONE
при вызове со старым, чтобы быть скрытым, фрагмент в качестве аргумента.Это также означает, что ваш адаптер всегда должен знать, какой фрагмент должен отображаться в позиции 0
FirstPageFragment
илиNextFragment
. Одним из способов сделать это является предоставление слушателя при созданииFirstPageFragment
, который будет вызываться, когда пришло время переключать фрагменты. Я думаю, что это хорошо, если ваш адаптер фрагментов обрабатывает все переключатели фрагментов и вызовыViewPager
иFragmentManager
.В-третьих,
FragmentPagerAdapter
кэширует использованные фрагменты по имени, которое является производным от позиции, поэтому, если в позиции 0 был фрагмент, он не будет заменен, даже если класс новый. Есть два решения, но самое простое - использоватьremove()
функциюFragmentTransaction
, которая также удалит свой тег.Это было много текста, вот код, который должен работать в вашем случае:
Надеюсь, это кому-нибудь поможет!
источник
FirstPageFragment.newInstance()
слушателя?По состоянию на 13 ноября 2012 года изменение фрагментов в ViewPager стало намного проще. Google выпустил Android 4.2 с поддержкой вложенных фрагментов, и он также поддерживается в новой библиотеке поддержки Android v11, так что это будет работать вплоть до 1.6
Это очень похоже на обычный способ замены фрагмента, за исключением того, что вы используете getChildFragmentManager. Кажется, это работает, за исключением того, что вложенный фрагмент backstack не появляется, когда пользователь нажимает кнопку возврата. В соответствии с решением в этом связанном вопросе вам нужно вручную вызвать popBackStackImmediate () для дочернего менеджера фрагмента. Поэтому вам нужно переопределить onBackPressed () действия ViewPager, где вы получите текущий фрагмент ViewPager и вызовете getChildFragmentManager (). PopBackStackImmediate ().
Получение фрагмента, отображаемого в данный момент, также немного странно, я использовал это грязное решение «android: switcher: VIEWPAGER_ID: INDEX», но вы также можете самостоятельно отслеживать все фрагменты ViewPager, как описано во втором решении на этой странице .
Итак, вот мой код для ViewPager с 4 ListViews с подробным представлением, отображаемым в ViewPager, когда пользователь щелкает строку и работает кнопка «Назад». Я попытался включить только соответствующий код для краткости, поэтому оставьте комментарий, если вы хотите, чтобы полное приложение было загружено на GitHub.
HomeActivity.java
ListProductsFragment.java
источник
Основываясь на ответе @wize, который я нашел полезным и элегантным, я смог добиться того, чего хотел частично, потому что хотел, чтобы возможность вернуться к первому фрагменту после замены. Я добился этого, немного изменив его код.
Это будет FragmentPagerAdapter:
Чтобы выполнить замену, просто определите статическое поле типа
CalendarPageFragmentListener
и инициализируйте с помощьюnewInstance
методов соответствующих фрагментов и вызовитеFirstFragment.pageListener.onSwitchToNextFragment()
илиNextFragment.pageListener.onSwitchToNextFragment()
соответственно.источник
mFragmentAtPos0
ссылку при сохранении состояния активности. Это не самое элегантное решение, но работает.Я реализовал решение для:
Уловки, чтобы достигнуть этого, следующие:
Код адаптера следующий:
При первом добавлении всех вкладок нам нужно вызвать метод createHistory (), чтобы создать исходную историю.
Каждый раз, когда вы хотите заменить фрагмент на конкретную вкладку, которую вы вызываете: replace (конечная позиция int, конечный класс ClassClass, конечные аргументы Bundle)
При нажатии back необходимо вызвать метод back ():
Решение работает с панелью действий Шерлока и жестом смахивания.
источник
tl; dr: используйте фрагмент хоста, который отвечает за замену размещенного контента и отслеживает историю обратной навигации (как в браузере).
Поскольку ваш вариант использования состоит из фиксированного количества вкладок, мое решение работает хорошо: идея состоит в том, чтобы заполнить ViewPager экземплярами пользовательского класса
HostFragment
, который может заменить размещенный контент и сохраняет собственную историю обратной навигации. Чтобы заменить размещенный фрагмент, вы вызываете методhostfragment.replaceFragment()
:Все, что делает этот метод, - это заменяет макет кадра идентификатором
R.id.hosted_fragment
на фрагмент, предоставленный методу.Проверьте мой учебник по этой теме для получения дополнительной информации и полного рабочего примера на GitHub!
источник
Некоторые из представленных решений очень помогли мне частично решить проблему, но в решениях по-прежнему отсутствует одна важная вещь, которая вызвала неожиданные исключения и содержимое черной страницы вместо содержимого фрагмента в некоторых случаях.
Дело в том, что класс FragmentPagerAdapter использует идентификатор элемента для хранения кэшированных фрагментов в FragmentManager . По этой причине вам также необходимо переопределить метод getItemId (int position), чтобы он возвращал, например, позицию для страниц верхнего уровня и позицию 100+ для страниц сведений. В противном случае ранее созданный фрагмент верхнего уровня будет возвращен из кэша вместо фрагмента уровня детализации.
Кроме того, я делюсь здесь полным примером того, как реализовать действия, подобные вкладкам, на страницах фрагментов с помощью ViewPager и кнопок вкладок с помощью RadioGroup, которая позволяет заменять страницы верхнего уровня подробными страницами, а также поддерживает кнопку возврата. Эта реализация поддерживает только один уровень бэк-стекирования (список элементов - подробности об элементе), но реализация многоуровневого бэк-стэкинга проста. Этот пример работает довольно хорошо в обычных случаях, за исключением того, что он генерирует исключение NullPointerException в случае, когда вы переключаетесь, например, на вторую страницу, изменяете фрагмент первой страницы (пока не виден) и возвращаетесь обратно на первую страницу. Я опубликую решение этой проблемы, как только выясню это:
источник
Я создал ViewPager с 3 элементами и 2 подэлементами для индекса 2 и 3, и вот что я хотел сделать ..
Я реализовал это с помощью предыдущих вопросов и ответов от StackOverFlow, и вот ссылка.
ViewPagerChildFragments
источник
Чтобы заменить фрагмент внутри,
ViewPager
вы можете переместить исходные кодыViewPager
,PagerAdapter
иFragmentStatePagerAdapter
классы в свой проект и добавьте следующий код.в
ViewPager
:в FragmentStatePagerAdapter:
handleGetItemInvalidated()
гарантирует, что после следующего вызоваgetItem()
вернет newFragmentgetFragmentPosition()
возвращает позицию фрагмента в вашем адаптере.Теперь для замены фрагментов звоните
Если вы заинтересованы в примере проекта, спросите меня об источниках.
источник
Прекрасно работает с решением AndroidTeam, однако я обнаружил, что мне нужна была возможность вернуться очень похоже на это.
FrgmentTransaction.addToBackStack(null)
Но простое добавление этого приведет только к замене фрагмента без уведомления ViewPager. Объединение предоставленного решения с этим незначительным улучшением позволит вам вернуться в предыдущее состояние, просто переопределивonBackPressed()
метод действия. Самым большим недостатком является то, что он будет возвращаться только по одному за раз, что может привести к нескольким обратным щелчкамНадеюсь, это кому-нибудь поможет.
Также, насколько
getFragmentPosition()
это возможно, это в значительной степениgetItem()
наоборот. Вы знаете, куда идут фрагменты, просто убедитесь, что вы вернете правильную позицию, в которой он будет находиться. Вот пример:источник
В твоем
onCreateView
методеcontainer
это на самом делеViewPager
экземпляр.Итак, просто звоню
изменит текущий фрагмент в вашем
ViewPager
.источник
vpViewPager.setCurrentItem(1);
, Я рассмотрел пример каждого человека, и каждый раз ничего не происходило, пока я, наконец, не дошел до твоего. Спасибо.ViewPager
другую страницу, он не заменит какой-либо фрагмент.Вот мое относительно простое решение этой проблемы. Ключи к этому решению следует использовать
FragmentStatePagerAdapter
вместо того, чтобы,FragmentPagerAdapter
поскольку первый удалит неиспользованные фрагменты для вас, в то время как последний все еще сохраняет свои экземпляры. Вторым является использованиеPOSITION_NONE
в getItem (). Я использовал простой список, чтобы отслеживать свои фрагменты. Мое требование состояло в том, чтобы заменить весь список фрагментов сразу новым списком, но ниже можно было легко изменить, чтобы заменить отдельные фрагменты:источник
Я также сделал решение, которое работает со стеками . Это более модульный подход, поэтому вам не нужно указывать каждый фрагмент и фрагмент в вашем
FragmentPagerAdapter
. Он построен поверх примера из ActionbarSherlock, который выводится, если я прав из демонстрационного приложения Google.Добавьте это для функциональности кнопки «Назад» в вашей MainActivity:
Если вы хотите сохранить состояние фрагмента, когда оно будет удалено. Позвольте вашему фрагменту реализовать
SaveStateBundle
возврат интерфейса в функции как связку с вашим состоянием сохранения. Получить комплект после создания экземпляраthis.getArguments()
.Вы можете создать вкладку следующим образом:
работает аналогично, если вы хотите добавить фрагмент поверх стека вкладок. Важно : я думаю, это не сработает, если вы захотите иметь 2 экземпляра одного класса поверх двух вкладок. Я быстро разработал это решение, поэтому могу поделиться им, не предоставляя никакого опыта.
источник
Замена фрагментов в окне просмотра довольно сложна, но очень возможна и может выглядеть супер гладко. Во-первых, вам нужно позволить самому viewpager обрабатывать удаление и добавление фрагментов. Что происходит, когда вы заменяете фрагмент внутри SearchFragment, ваш viewpager сохраняет свои представления фрагментов. Таким образом, вы получаете пустую страницу, потому что SearchFragment удаляется, когда вы пытаетесь заменить ее.
Решение состоит в том, чтобы создать слушатель внутри вашего viewpager, который будет обрабатывать изменения, сделанные вне него, поэтому сначала добавьте этот код в нижнюю часть вашего адаптера.
Затем вам нужно создать закрытый класс в вашем viewpager, который становится слушателем, когда вы хотите изменить свой фрагмент. Например, вы можете добавить что-то вроде этого. Обратите внимание, что он реализует интерфейс, который был только что создан. Поэтому всякий раз, когда вы вызываете этот метод, он запускает код внутри класса ниже.
Здесь следует отметить две основные вещи:
Обратите внимание на слушателей, которые размещены в конструкторе newInstance (слушатель). Вот как вы будете вызывать fragment0Changed (String newFragmentIdentification) `. Следующий код показывает, как вы создаете слушателя внутри вашего фрагмента.
static nextFragmentListener listenerSearch;
Вы можете назвать изменения внутри вашего
onPostExecute
Это приведет к тому, что код внутри вашего viewpager переключит ваш фрагмент в нулевую позицию fragAt0, чтобы стать новым searchResultFragment. Есть еще две маленькие части, которые вам нужно добавить в пейджер, прежде чем он станет функциональным.
Один из них будет в методе переопределения getItem viewpager.
Теперь без этой последней части вы все равно получите пустую страницу. Вид хромает, но это неотъемлемая часть viewPager. Вы должны переопределить метод getItemPosition окна просмотра. Обычно этот метод возвращает POSITION_UNCHANGED, который сообщает ViewPager о том, что все должно быть одинаково, и поэтому getItem никогда не будет вызываться для размещения нового фрагмента на странице. Вот пример того, что вы могли бы сделать
Как я уже сказал, код становится очень сложным, но вам нужно создать собственный адаптер для вашей ситуации. Вещи, которые я упомянул, позволят изменить фрагмент. Скорее всего, потребуется много времени, чтобы все впитать, поэтому я буду терпеливым, но все это будет иметь смысл. Это стоит того, чтобы потратить время, потому что это может сделать действительно привлекательное приложение.
Вот самородок для обработки кнопки возврата. Вы помещаете это в свой MainActivity
Вам нужно будет создать метод с именем backPressed () внутри FragmentSearchResults, который будет вызывать Frag0знак. Это в сочетании с кодом, который я показал ранее, будет обрабатывать нажатие кнопки назад. Удачи вам в вашем коде для изменения виджета. Требуется много работы, и, насколько я обнаружил, нет никаких быстрых адаптаций. Как я уже сказал, вы в основном создаете собственный адаптер viewpager и позволяете ему обрабатывать все необходимые изменения с помощью слушателей
источник
Это мой способ достичь этого.
Прежде всего добавьте
Root_fragment
внутреннююviewPager
вкладку, в которой вы хотите реализоватьfragment
событие нажатия кнопки . Пример;Прежде всего,
RootTabFragment
следует включитьFragmentLayout
для смены фрагмента.Затем, внутри
RootTabFragment
onCreateView
, реализоватьfragmentChange
для вашегоFirstPagerFragment
После этого реализуйте
onClick
событие для вашей кнопки внутриFirstPagerFragment
и снова внесите изменение фрагмента.Надеюсь, это поможет тебе, парень.
источник
Я нашел простое решение, которое отлично работает, даже если вы хотите добавить новые фрагменты посередине или заменить текущий фрагмент. В моем решении вы должны переопределить
getItemId()
который должен возвращать уникальный идентификатор для каждого фрагмента. Не позиционируется как по умолчанию.Вот оно:
Примечание: в этом примере
FirstFragment
иSecondFragment
расширяется абстрактный класс PageFragment, который имеет методgetPage()
.источник
Я делаю что-то похожее на wize, но в моем ответе вы можете переключаться между двумя фрагментами, когда захотите. И с мудрым ответом у меня есть некоторые проблемы при изменении ориентации экрана и тому подобное. Это выглядит как PagerAdapter:
Слушатель, который я реализовал в контейнере адаптера, чтобы прикрепить его к фрагменту при присоединении, это действие:
Затем во фрагмент помещают слушателя, когда прикрепляют вызывающий его:
И наконец слушатель:
источник
Я последовал за ответами @wize и @mdelolmo, и я получил решение. Спасибо, Тонны. Но я немного настроил эти решения, чтобы улучшить потребление памяти.
Проблемы, которые я заметил:
Они сохраняют экземпляр
Fragment
которого заменяется. В моем случае это фрагмент,MapView
и я подумал, что это дорого. Итак, я поддерживаюFragmentPagerPositionChanged (POSITION_NONE or POSITION_UNCHANGED)
вместоFragment
себя.Вот моя реализация.
Демонстрационная ссылка здесь .. https://youtu.be/l_62uhKkLyM
Для демонстрации цели использовали 2 фрагмента
TabReplaceFragment
иDemoTab2Fragment
в позиции два. Во всех остальных случаях я используюDemoTabFragment
экземпляры.Объяснение:
Я перехожу
Switch
от деятельности кDemoCollectionPagerAdapter
. На основе состояния этого переключателя мы будем отображать правильный фрагмент. Когда проверка переключателей, я звонюSwitchFragListener
«SonSwitchToNextFragment
метод, где я меняющийся значениеpagerAdapterPosChanged
переменной кPOSITION_NONE
. Узнайте больше о POSITION_NONE . Это лишит законной силы getItem, и у меня есть логика для создания правильного фрагмента там. Извините, если объяснение немного грязное.Еще раз большое спасибо @wize и @mdelolmo за оригинальную идею.
Надеюсь, это полезно. :)
Дайте мне знать, если эта реализация имеет какие-либо недостатки. Это будет очень полезно для моего проекта.
источник
после исследования я нашел решение с коротким кодом. прежде всего создайте публичный экземпляр для фрагмента и просто удалите ваш фрагмент в onSaveInstanceState, если фрагмент не воссоздается при изменении ориентации.
источник