tutorial studio pasar otro navegar llamar fragments example entre dinamicos desde boton atras activity android android-fragments android-viewpager android-lifecycle

pasar - navegar entre fragments android studio



Los fragmentos de ViewPager no se vuelven a crear después de FragmentTransaction.replace() seguido del botón Atrás (2)

Después de luchar con esto durante unos días, me di cuenta de que el problema en realidad era que pasé el FragmentManager incorrecto a ViewPager . De hecho, es el FragmentManager devuelto por Fragment.getChildFragmentManager() que debe utilizarse. Esto tiene sentido, ya que es el Fragment mismo el que almacena el estado del Fragment hijo en el ViewPager . No estoy seguro de por qué no pude hacer ese trabajo antes, pero ahora el LifeCycle de la aplicación funciona bien con la siguiente configuración en el método onCreate() mi Fragment principal:

@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.mm_main_fragment, container, false); mMMActivity = (MMActivity) container.getContext(); mMMActivity.getSupportActionBar().setTitle(getString(R.string.mm_toolbar_title_main_fragment)); // Setup ViewPager. mViewPager = (ViewPager) view.findViewById(R.id.mm_pager); mAdapter = new MMPagerAdapter(getChildFragmentManager(), mMMActivity); // <-- This is the key mViewPager.setAdapter(mAdapter); mViewPager.setOffscreenPageLimit(PagerConstants.OFFSCREEN_PAGE_LIMIT); mViewPager.setCurrentItem(PagerConstants.PAGE_FILTER_RECIPES); // Setup TabLayout. TabLayout tabLayout = (TabLayout) view.findViewById(R.id.mm_tablayout); tabLayout.setupWithViewPager(mViewPager); return view; }

Esta fue también la solución de otra pregunta . Como nota al margen, utilizo esta solución para obtener los Fragment en mi ViewPager y funciona muy bien con LifeCycle.

Aplicación de prueba:

En la búsqueda de este problema, creé una aplicación de prueba para esto. Los interesados ​​pueden clonarlo desde here . Tiene colores feos.

Estoy tratando de implementar el estado de guardar y restaurar, pero tengo problemas al reemplazar el Fragment principal con un Fragment PreferenceFragment y luego presionar el botón Atrás. Mi Fragment principal consiste en una ViewPager con un FragmentPagerAdapter con 3 Fragments para deslizar. Ninguna de las devoluciones de llamada Fragment.onCreateView() para mis 3 Fragment se invoca después de presionar el botón Atrás. He intentado todas las soluciones que he venido aquí en SO, pero no he podido resolver el problema.

Otra cosa posiblemente importante a tener en cuenta es que los datos para mis 3 Fragment ViewPager se almacenan en clases separadas que se instancian y se puede acceder a través de la Activity . Los 3 Fragment contienen todos RecyclerView s para enumerar una buena cantidad de datos. Esto se hace por limpieza, pero también para que estos datos persistan en la Activity . Probablemente esto no sea un problema, ya que funciona bien al iniciar la aplicación, pero también porque el problema principal es que mis Fragments no se vuelven a crear.

Comportamiento inesperado:

En la creación de aplicaciones, todo funciona bien, pero cuando reemplazo mi Fragment principal (que contiene un ViewPager con un FragmentPagerAdapter ) por otro y luego presiono el botón Atrás, los Fragment de ViewPager no se ViewPager crear. Se onCreateView() de mi Fragment principal.

Preguntas:

¿Qué me estoy perdiendo? ¿Hay alguna otra devolución de llamada que deba ser creada? ¿Dónde y cómo debería recrear los Fragments ?

¿Qué he intentado?

  1. Cambié el FragmentAdapter en uso, pero realmente debería usar getSupportFragmentManager() como fue la solución here .

EDITAR: El uso de FragmentManager erróneo realmente fue la fuente de mis problemas. Ver mi respuesta a continuación.

  1. Agregue @Override public int getItemPosition(Object object) {return POSITION_NONE;} al FragmentPagerAdapter , como se sugiere here .
  2. Cambie de FragmentPagerAdapter a FragmentStatePagerAdapter que no debería importar aquí, por lo que yo entiendo. Las partes internas de FragmentStatePagerAdapter realmente conservan los Fragments , pero no se representan de nuevo.
  3. Agrega el Fragment principal de nuevo usando FragmentTransaction cuando se llama a onCreateView() y en varias de las otras devoluciones de llamada de ese Fragment , mira MMMainFragment continuación.
  4. Varias otras cosas.

Mi clase MainFragment con el diseño XML correspondiente

public class MMMainFragment extends Fragment { private MMViewPager mMMViewPager = null; @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Note that this method IS called when the back button is pressed. // I have tried setting the content back to this instance in several places. View view = inflater.inflate(R.layout.mm_main_fragment, container, false); // Setup ViewPager. mMMViewPager = (MMViewPager) view.findViewById(R.id.mm_pager); TabLayout tabLayout = (TabLayout) view.findViewById(R.id.mm_tablayout); tabLayout.setupWithViewPager(mMMViewPager); return view; } public MMViewPager getMMViewPager() { return mMMViewPager; } }

mm_main_fragment.xml

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" > <android.support.design.widget.TabLayout android:id="@+id/mm_tablayout" android:layout_width="match_parent" android:layout_height="wrap_content" app:tabMode="scrollable" /> <com.mycompany.myapp.gui.mmpager.MMViewPager android:id="@+id/mm_pager" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1" /> </LinearLayout>

Luego, en Activity.onCreate() simplemente hago:

// Insert Main Fragment. FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager.beginTransaction() .replace(R.id.mm_content_frame, new MMMainFragment()) .commit();

donde mm_content_frame es FrameLayout donde reemplazo y FrameLayout Fragment s. Luego, cuando se presiona un botón para ver las preferencias, ejecuto el siguiente fragmento a continuación en la Activity . Agregué este Fragment a la parte posterior para poder usar el botón Atrás.

public void showSettingsFragment() { getSupportActionBar().setTitle(getString(R.string.mm_drawer_settings)); getSupportFragmentManager().beginTransaction() .replace(R.id.mm_content_frame, new MMPreferencesFragment(), FragmentConstants.SETTINGS_FRAGMENT_TAG) .addToBackStack(null) .commit(); }

La clase MMViewPager:

public class MMViewPager extends ViewPager { private MMActivity mMMActivity; private MMPagerAdapter mMMPagerAdapter; public MMViewPager(Context context, AttributeSet attrs) { super(context, attrs); mMMActivity = (MMActivity) context; mMMPagerAdapter = new MMPagerAdapter(mMMActivity.getSupportFragmentManager(), mMMActivity); this.setOffscreenPageLimit(PagerConstants.OFFSCREEN_PAGE_LIMIT); this.setAdapter(mMMMPagerAdapter); this.setCurrentItem(PagerConstants.PAGE_FILTER_RECIPES); } }

La clase MMPagerAdapter, actualmente un FragmentStatePagerAdapter :

public class MMPagerAdapter extends FragmentStatePagerAdapter { private MMActivity mMMActivity; public MMPagerAdapter(FragmentManager fragmentManager, MMActivity mmActivity) { super(fragmentManager); mMMActivity = mmActivity; } @Override public Fragment getItem(int position) { switch (position) { case PagerConstants.PAGE_FILTER_RECIPES: return new FilteredRecipesFragment(); case PagerConstants.PAGE_SELECTED_RECIPES: return new SelectedRecipesFragment(); case PagerConstants.PAGE_SHOPPING_LIST: return new ShoppingListFragment(); default: return null; } } @Override public int getCount() { return PagerConstants.NUMBER_OF_PAGES; // 3 } @Override public CharSequence getPageTitle(int position) { switch (position) { case PagerConstants.PAGE_FILTER_RECIPES: return mMMActivity.getResources().getString(R.string.mm_title_recipe_filter_fragment); case PagerConstants.PAGE_SELECTED_RECIPES: return mMMActivity.getResources().getString(R.string.mm_title_selected_recipes_fragment); case PagerConstants.PAGE_SHOPPING_LIST: return mMMActivity.getResources().getString(R.string.mm_title_shopping_list_fragment); default: return "Tab"; } } }

Como referencia, aquí está también el diseño principal de la Activity :

<?xml version="1.0" encoding="utf-8"?> <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/mm_drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- Source: http://developer.android.com/training/implementing-navigation/nav-drawer.html --> <!-- The main content view --> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <!-- Source: http://android-developers.blogspot.in/2014/10/appcompat-v21-material-design-for-pre.html --> <android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/mm_toolbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="?attr/actionBarSize" android:layout_alignParentTop="true" style="@style/MMActionBar" app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" app:popupTheme="@style/ThemeOverlay.AppCompat.Light" /> <!-- The FrameLayout where I replace Fragments --> <FrameLayout android:id="@+id/mm_content_frame" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@id/mm_toolbar"/> </RelativeLayout> <ListView android:id="@+id/mm_drawer_listview" android:layout_width="260dp" android:layout_height="match_parent" android:paddingLeft="16dp" android:paddingRight="16dp" android:paddingStart="16dp" android:layout_gravity="start" android:choiceMode="singleChoice" android:divider="@android:color/transparent" android:dividerHeight="0dp" android:background="@color/mm_white" /> </android.support.v4.widget.DrawerLayout>


En cuanto a, entiendo que hayas resuelto tu problema. Me he enfrentado a un problema bastante similar en el pasado. Tal vez esto ayude a otros a intentar algo similar.

Lo que me ayudó para retener mi fragmento fue reemplazar el

@Override public Fragment getItem(int position) { switch (position) { case PagerConstants.PAGE_FILTER_RECIPES: return new FilteredRecipesFragment(); case PagerConstants.PAGE_SELECTED_RECIPES: return new SelectedRecipesFragment(); case PagerConstants.PAGE_SHOPPING_LIST: return new ShoppingListFragment(); default: return null; } }

método de mi adaptador.

Cambio el Adapter para mantener un HashMap de Fragment . De esta forma, los fragmentos se crean una vez y luego se recuperan de HashMap. Según su estructura, puede usar una ArrayList lugar de HashMap.

Esto se hace de una manera eficiente e "inteligente" de implementar getItemPosition(Object object) para return POSITION_NONE; o la posición correcta del artículo.

Realmente no recuerdo si tenía el getChildFragmentManager en el adaptador o no.

Supongo que podría profundizar en mi sistema de archivos si necesitas más información :)