Замена ViewPager на фрагмент - затем переход назад
У меня есть активность, которая изначально содержит ViewPager, подключен к FragmentPagerAdapter.
Когда пользователь нажимает на элемент внутри дочернего фрагмента ViewPager, я использую FragmentTransaction, чтобы заменить пустой контейнерный вид новым фрагментом, к которому я хочу перейти.
Если я использую addToBackStack() для транзакции, совершите транзакцию, а затем вернитесь назад, я не вернусь в представления ViewPager (исходный макет).
Если я не использую addToBackStack() для транзакции, зафиксируйте транзакцию и затем вернитесь назад, приложение завершает работу.
Кажется очевидным, что ViewPager не добавлен в backstack (что не удивительно, поскольку это не фрагмент сам по себе). Но я ожидаю, что поведение по умолчанию будет состоять в том, что обратная печать возвращает меня обратно, который выполняет начальный вид View (ViewPager).
На основании того, что я прочитал, кажется, что, возможно, из-за того, что происходит транзакция фрагмента, ViewPager или PagerAdapter теряет информацию о том, какой фрагмент должен отображаться.
Я действительно смущен этим, но в итоге я создал огромный беспорядок кода, переопределяя onBackPress, показывая и скрывая представления viewpager. Я бы подумал, что есть простой способ использовать поведение по умолчанию для выполнения соответствующей навигации.
ТЛ; дг
A - фрагменты хоста Viewpager.
B - новый фрагмент.
Когда я заменяю A на B, а затем нажимаю назад, я ожидаю, что вернусь к A, но этого не произойдет.
Любые советы будут высоко оценены.
код:
MainActivity:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
headingLayout = (RelativeLayout) findViewById(R.id.headingLayout);
headingLayout.setVisibility(View.GONE);
// Set up the ViewPager, attaching the adapter and setting up a listener
// for when the
// user swipes between sections.
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setPageMargin(8);
/** Getting fragment manager */
FragmentManager fm = getSupportFragmentManager();
/** Instantiating FragmentPagerAdapter */
MyFragmentPagerAdapter pagerAdapter = new MyFragmentPagerAdapter(fm);
/** Setting the pagerAdapter to the pager object */
mViewPager.setAdapter(pagerAdapter);
.
.
.
}
public void onListItemClicked(Fragment fragment) {
fromPlayer = false;
InitiateTransaction(fragment, true);
}
public void InitiateTransaction(Fragment fragment, boolean addToBackStack) {
invalidateOptionsMenu();
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.fragmentContainer, fragment).addToBackStack(null)
.commit();
}
PagerAdapter:
package another.music.player;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import another.music.player.fragments.AlbumListFragment;
import another.music.player.fragments.ArtistListFragment;
import another.music.player.fragments.SongListFragment;
public class MyFragmentPagerAdapter extends FragmentPagerAdapter {
final int PAGE_COUNT = 3;
/** Constructor of the class */
public MyFragmentPagerAdapter(FragmentManager fm) {
super(fm);
}
/** This method will be invoked when a page is requested to create */
@Override
public Fragment getItem(int i) {
switch (i) {
case 0:
ArtistListFragment artistListFragment = new ArtistListFragment();
Bundle artistData = new Bundle();
artistData.putInt("current_page", i + 1);
artistListFragment.setArguments(artistData);
return artistListFragment;
case 1:
AlbumListFragment albumListFragment = new AlbumListFragment();
Bundle albumData = new Bundle();
albumData.putInt("current_page", i + 1);
albumData.putBoolean("showHeader", false);
albumListFragment.setArguments(albumData);
return albumListFragment;
default:
SongListFragment songListFragment = new SongListFragment();
Bundle songData = new Bundle();
songData.putInt("current_page", i + 1);
songListFragment.setArguments(songData);
return songListFragment;
}
}
/** Returns the number of pages */
@Override
public int getCount() {
return PAGE_COUNT;
}
@Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return "Artists";
case 1:
return "Albums";
default:
return "Songs";
}
}
}
main xml (содержащий фрагментContainer и ViewPager):
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/app_background_ics" >
<RelativeLayout
android:id="@+id/headingLayout"
android:layout_width="match_parent"
android:layout_height="56dp" >
</RelativeLayout>
<FrameLayout
android:id="@+id/fragmentContainer"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="@+id/headingLayout" />
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<android.support.v4.view.PagerTabStrip
android:id="@+id/pager_title_strip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#33b5e5"
android:paddingBottom="4dp"
android:paddingTop="4dp"
android:textColor="#fff" />
</android.support.v4.view.ViewPager>
</RelativeLayout>
Ответы
Ответ 1
У меня также была такая же проблема в течение длительного времени. Решение оказалось очень простым, и вам не нужны хаки с ViewPager Visibility. Я описываю в этом другом вопросе, относящемся к SO: Фрагмент в ViewPager не восстановлен после popBackStack
Однако, чтобы сделать его простым, все, что вам нужно, это использовать getChildFragmentManager() в вашем адаптере ViewPager вместо getSupportFragmentManager(). Итак, вместо этого:
/** Getting fragment manager */
FragmentManager fm = getSupportFragmentManager();
/** Instantiating FragmentPagerAdapter */
MyFragmentPagerAdapter pagerAdapter = new MyFragmentPagerAdapter(fm);
/** Setting the pagerAdapter to the pager object */
mViewPager.setAdapter(pagerAdapter);
Вы делаете это:
/** Getting fragment manager */
FragmentManager fm = getChildFragmentManager();
/** Instantiating FragmentPagerAdapter */
MyFragmentPagerAdapter pagerAdapter = new MyFragmentPagerAdapter(fm);
/** Setting the pagerAdapter to the pager object */
mViewPager.setAdapter(pagerAdapter);
Ответ 2
ОБНОВЛЕНИЕ:
Это не "Android-способ", и это приводит к плохой работе с пользователями в случае списка. Вместо этого создайте новое действие.
Для людей, которые ищут простое решение этой проблемы, я просто подытожу то, что я сделал.
Моя архитектура:
Для каждого FragmentContainer мы вызываем getChildFragmentManager в методе onCreate, например, и добавляем фрагмент, который мы хотим показать в этом контейнере:
FragmentToShow fragment = new FragmentToShow();
getChildFragmentManager()
.beginTransaction()
.add(R.id.container, fragment)
.commit();
Мы не хотим, чтобы наш первый фрагмент добавлялся в стопку контейнера фрагмента, потому что мы не хотим показывать контейнер фрагмента, если мы нажмем кнопку "Назад" .
Затем, если мы хотим заменить FragmentToShow другим фрагментом в нашем классе FragmentToShow (например, с помощью listView):
Fragment itemFragment = new ItemFragment();
getFragmentManager()
.beginTransaction()
.replace(R.id.container, itemFragment)
.addToBackStack(null)
.commit();
Здесь мы извлекаем диспетчер дочерних фрагментов и добавляем itemFragment в задний стек.
Итак, теперь мы хотим, нажав кнопку "Назад" , вернуться к спискуView (экземпляр FragmentToShow). Наша активность (FragmentActivity) является единственной, кто знает кнопку "Назад" , поэтому мы должны переопределить метод onBackPressed() в этом действии:
@Override
public void onBackPressed() {
// We retrieve the fragment manager of the activity
FragmentManager frgmtManager = getSupportFragmentManager();
// We retrieve the fragment container showed right now
// The viewpager assigns tags to fragment automatically like this
// mPager is our ViewPager instance
Fragment fragment = frgmtManager.findFragmentByTag("android:switcher:" + mPager.getId() + ":" + mPager.getCurrentItem());
// And thanks to the fragment container, we retrieve its child fragment manager
// holding our fragment in the back stack
FragmentManager childFragmentManager = fragment.getChildFragmentManager();
// And here we go, if the back stack is empty, we let the back button doing its job
// Otherwise, we show the last entry in the back stack (our FragmentToShow)
if(childFragmentManager.getBackStackEntryCount() == 0){
super.onBackPressed();
} else {
childFragmentManager.popBackStack();
}
}
Так как мы вызываем getSupportFragmentManager в нашей деятельности, мы просто можем вызвать getFragmentManager в наших дочерних фрагментах. Это вернет экземпляр поддержки FragmentManager.
И это! Я не эксперт, поэтому, если у вас есть предложения или замечания, не стесняйтесь.
Ответ 3
Единственный способ, который я нашел для этого, - сделать следующее:
При навигации от viewPager отправьте viewPager вне поля зрения с помощью Visiblity.GONE. Добавьте транзакции фрагмента в стопку.
При возврате на экран viewPager (через обратное нажатие) переопределите onBackPressed. Вы можете проверить, сколько фрагментов находится в задней части. Если viewPager был первым представлением до того, как произошли транзакции фрагмента, вы можете проверить, есть ли счетчик записей в backstack count 0.
fragmentManager.getBackStackEntryCount() == 0, в задней части нет фрагментов.
Если это утверждение верно, то просто верните viewPager в поле зрения с помощью Visibility.VISIBLE.
Ответ 4
Если вы используете ViewPager
содержащий Fragments
, который может запуститься Activities
, вот как вы правильно вернетесь в позицию в ViewPager
, после того, как вы вернетесь или перейдете к указанному Activity
(Предположим, что для вашего Activities
объявлена восходящая навигация).
Сначала вам нужно передать текущую позицию ViewPager
в качестве дополнительного в Intent
, чтобы запустить новый Activity
.
Затем вы передадите эту позицию родительскому Activity
, выполнив следующее:
Intent upIntent = NavUtils.getParentActivityIntent(this);
upIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
upIntent.putExtra(FeedPagerActivity.EXTRA_POSITION, position);
NavUtils.navigateUpTo(this, upIntent);
Поместите этот код внутри
onOptionsItemSelected (элемент MenuItem)
Ответ 5
Я думаю, что у меня была очень похожая проблема.
Кажется, что FragmentManager
ведет себя несколько иерархически. То, как я решил эту проблему, заключалось в том, чтобы использовать "основной" FragmentManager из действия, который размещает контейнер и ViewPager, а не тот, который может быть извлечен из фрагментов внутри ViewPager.
Для этого я использовал:
this.getActivity().getSupportFragmentManager()
где this
- это экземпляр фрагмента.
Теперь, если я перехожу из элемента в ViewPager к другому фрагменту с транзакцией "replace", по возвращении я могу видеть ViewPager в состоянии, которое у меня осталось.
Более полный пример кода выглядит следующим образом:
FragmentManager fragmentManager = MyCurrentFragment.this.getActivity().getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.container, newFragment);
transaction.addToBackStack(null);
transaction.commit();