android - studio - Reemplazando ViewPager con Fragment-Luego navegando hacia atrás
sectionspageradapter (5)
Tengo una actividad que inicialmente aloja un ViewPager, conectado a un FragmentPagerAdapter.
Cuando el usuario hace clic en un elemento dentro del fragmento hijo de ViewPager, estoy usando FragmentTransaction para reemplazar una vista de contenedor vacía con un nuevo Fragment al que quiero navegar.
Si utilizo addToBackStack () en la transacción, confirmo la transacción y luego navego hacia atrás, no vuelvo a las vistas de ViewPager (el diseño inicial).
Si no uso addToBackStack () en la transacción, confirmo la transacción y luego navego hacia atrás, la aplicación sale.
Parece evidente que ViewPager no se agrega a la backstack (lo cual no es sorprendente, ya que no es un fragmento en sí mismo). Pero esperaría que el comportamiento predeterminado fuera que la retroactividad me lleve de vuelta a las actividades iniciales. Ver (el ViewPager).
Basado en lo que he leído, parece que quizás debido a que se está llevando a cabo una transacción de fragmento, ViewPager o PagerAdapter pierde la pista de qué fragmento debería estar en exhibición.
Estoy realmente confundido con esto, pero terminé creando un desorden enorme de código anulando el onBackPress y mostrando y ocultando las vistas del visor. Hubiera pensado que hay una forma más simple de usar comportamientos predeterminados para realizar la navegación adecuada.
tl; dr
A es un fragmento de alojamiento de Viewpager. B es un nuevo Fragmento.
Cuando reemplazo A con B y luego presiono hacia atrás, espero volver a A, pero eso no está sucediendo.
Cualquier consejo sería muy apreciado.
Código:
Actividad principal:
@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";
}
}
}
xml principal (que contiene fragmentContainer y 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>
Creo que tuve un problema muy similar.
Parece que FragmentManager
s se comporta de una manera un tanto jerárquica. La forma en que resolví este problema fue utilizar el FragmentManager "principal" de la actividad, que hospeda el contenedor y el ViewPager, y no el que se puede recuperar de los fragmentos dentro de ViewPager.
Para hacer esto, he usado:
this.getActivity().getSupportFragmentManager()
donde this
es una instancia de Fragmento.
Ahora, si navego desde un elemento en la ViewPager a otro fragmento con la transacción "reemplazar", al volver puedo ver la ViewPager en el estado que he dejado.
El ejemplo de código más completo se ve así:
FragmentManager fragmentManager = MyCurrentFragment.this.getActivity().getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.container, newFragment);
transaction.addToBackStack(null);
transaction.commit();
La única forma que he encontrado para lograr esto es hacer lo siguiente:
Cuando navegue desde viewPager, envíe viewPager fuera de la vista con Visiblity.GONE. Agregue cualquier transacción de fragmento a la backstack.
Cuando regrese a la pantalla viewPager (a través de una presión atrás), anule el onBackPressed. Puedes verificar cuántos fragmentos hay en la backstack. Si viewPager fue la primera vista antes de que se llevaran a cabo las transacciones de fragmento, entonces puede verificar si el número de entrada de fragmento de backstack es 0.
fragmentManager.getBackStackEntryCount () == 0, no hay fragmentos en la backstack.
Si esa declaración es verdadera, simplemente vuelva a visualizar viewPager usando Visibility.VISIBLE.
Si está usando una ViewPager
contiene Fragments
que pueden iniciar Activities
, aquí es cómo debería volver a la posición en ViewPager
, al retroceder o al navegar desde dicha Activity
(Asume que tiene Navegación hacia arriba declarada para sus Activities
).
En primer lugar, debe pasar la posición actual de ViewPager
como Extra en el Intent
para iniciar la nueva Activity
.
Luego, devolverá esa posición a la Activity
principal haciendo esto:
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);
Pon ese código dentro
onOptionsItemSelected (elemento MenuItem)
También tuve este mismo problema durante mucho tiempo. La solución resulta ser muy simple, y no necesita ningún ataque con ViewPager Visibility. I se describe en esta otra pregunta relacionada con SO: Fragmento en ViewPager no restaurado después de popBackStack
Sin embargo, para hacerlo simple, todo lo que necesita es usar getChildFragmentManager () en su adaptador ViewPager, en lugar de getSupportFragmentManager (). Entonces, en lugar de esto:
/** Getting fragment manager */
FragmentManager fm = getSupportFragmentManager();
/** Instantiating FragmentPagerAdapter */
MyFragmentPagerAdapter pagerAdapter = new MyFragmentPagerAdapter(fm);
/** Setting the pagerAdapter to the pager object */
mViewPager.setAdapter(pagerAdapter);
Tu hiciste esto:
/** Getting fragment manager */
FragmentManager fm = getChildFragmentManager();
/** Instantiating FragmentPagerAdapter */
MyFragmentPagerAdapter pagerAdapter = new MyFragmentPagerAdapter(fm);
/** Setting the pagerAdapter to the pager object */
mViewPager.setAdapter(pagerAdapter);
ACTUALIZAR:
Esa no es la "manera de Android" y resulta en una mala experiencia de usuario para el caso de una lista. En cambio, crea una nueva actividad.
Para las personas que buscan una solución simple a este problema, resumiré lo que hice.
Mi arquitectura:
ViewPager en FragmentActivity (ActionBarActivity en realidad, para el soporte de ActionBar. Pero ActionBarActivity implementa FragmentActivity).
2 pestañas:
- FragmentContainer1 que extiende Fragment.
- FragmentContainer2 que extiende Fragment.
Para cada FragmentContainer, llamamos a getChildFragmentManager, en el método onCreate por ejemplo, y agregamos el fragmento que queremos mostrar en este contenedor:
FragmentToShow fragment = new FragmentToShow();
getChildFragmentManager()
.beginTransaction()
.add(R.id.container, fragment)
.commit();
No queremos que nuestro primer fragmento se agregue al backstack del contenedor de fragmentos porque no queremos mostrar el contenedor de fragmentos si presionamos el botón Atrás.
Entonces, si queremos reemplazar FragmentToShow por otro fragmento en nuestra clase FragmentToShow (como con un listView):
Fragment itemFragment = new ItemFragment();
getFragmentManager()
.beginTransaction()
.replace(R.id.container, itemFragment)
.addToBackStack(null)
.commit();
Aquí recuperamos el administrador de fragmentos hijo y agregamos el itemFragment a la pila posterior.
Así que ahora queremos, al presionar el botón Atrás, volver a ListView (la instancia de FragmentToShow). Nuestra actividad (FragmentActivity) es la única que conoce el botón Atrás, por lo que debemos anular el método onBackPressed () en esta actividad:
@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();
}
}
Como llamamos a getSupportFragmentManager en nuestra actividad, solo podemos llamar a getFragmentManager en nuestros fragmentos secundarios. Esto devolverá una instancia de FragmentManager de soporte.
¡Y eso es! No soy un experto, así que si tiene sugerencias o comentarios, siéntase libre.