29

Tengo una actividad que inicialmente aloja un ViewPager, conectado a un FragmentPagerAdapter.Reemplazo de ViewPager con Fragment - Luego navegando hacia atrás

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 Fragmento al que quiero navegar.

Si uso 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 retroalimentación me lleva de vuelta a esa vista inicial de actividades (la ViewPager).

Según 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 ViewPager fragmentos de alojamiento. 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:

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"; 
     } 
    } 
} 

xml principal (que contiene fragmentContainer & 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> 

Respuesta

3

La única forma que he encontrado para lograr esto es hacer lo siguiente:

Al navegar fuera de la viewPager, envíe la viewPager fuera de la vista usando Visiblity.GONE. Agregue cualquier transacción de fragmento a la backstack.

Al volver a la pantalla viewPager (mediante una presión atrás), anula el onBackPressed. Puedes verificar cuántos fragmentos hay en la backstack. Si viewPager fue la primera vista antes de que se realizaran las transacciones de fragmento, puede verificar si el recuento de entrada de fragmento de backstack es 0.

fragmentManager.getBackStackEntryCount() == 0, no hay fragmentos en la copia posterior.

Si esa afirmación es verdadera, simplemente vuelva a mostrar viewPager con Visibility.VISIBLE.

+1

SÍ! Gracias. –

33

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: Fragment in ViewPager not restored after popBackStack

Sin embargo, para hacerlo simple, todo lo que necesita es usar getChildFragmentManager() en su adaptador ViewPager, en lugar de getSupportFragmentManager(). Así, 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); 

Para ello:

/** Getting fragment manager */ 
    FragmentManager fm = getChildFragmentManager(); 

    /** Instantiating FragmentPagerAdapter */ 
    MyFragmentPagerAdapter pagerAdapter = new MyFragmentPagerAdapter(fm); 

    /** Setting the pagerAdapter to the pager object */ 
    mViewPager.setAdapter(pagerAdapter); 
+9

getChildFragmentManager() está bien si su ViewPager está alojado dentro de un Fragment, pero no si ViewPager está alojado dentro de FragmentActivity. –

+0

De nada :) –

9

ACTUALIZACIÓN:
Ese no es el "camino Android" y da lugar a mala experiencia de usuario para el caso de una vista de 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 realidad, para el apoyo Barra de acciones Pero ActionBarActivity implementa FragmentActivity.).

  • 2 pestañas:

    • FragmentContainer1 que se extiende Fragmento.
    • FragmentContainer2 que extiende Fragment.

Para cada FragmentContainer, llamamos getChildFragmentManager, en el método onCreate por ejemplo, y añadir 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 Se agregará un fragmento al backstack del contenedor de fragmentos porque no queremos mostrar el contenedor de fragmentos si presionamos el botón Atrás.

Entonces, si queremos sustituir FragmentToShow por otro fragmento de nuestra clase FragmentToShow (como con una vista de lista):

Fragment itemFragment = new ItemFragment(); 

getFragmentManager() 
     .beginTransaction() 
     .replace(R.id.container, itemFragment) 
     .addToBackStack(null) 
     .commit(); 

Aquí recuperar el gestor de fragmento de niño, y le añadimos el itemFragment a la pila de vuelta .

Así que ahora queremos, al presionar el botón Atrás, volver a ListView (la instancia de FragmentToShow). Nuestra actividad (FragmentActivity) es el único consciente del botón de retroceso, así que tenemos que reemplazar 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(); 
    } 
} 

Desde que llamamos getSupportFragmentManager en nuestra actividad, que sólo se puede llamar getFragmentManager en nuestros fragmentos de niño Esto devolverá una instancia de FragmentManager de soporte.

¡Y eso es todo! No soy un experto, así que si tiene sugerencias o comentarios, siéntase libre.

+0

Muchas gracias onBackPressed() funciona bien. – Androidbirt

+0

Esta arquitectura está trabajando para mí. Gracias :) – Dory

0

Si está utilizando un ViewPager contiene Fragments que puede comenzar Activities, aquí es cómo le adecuadamente navegar de vuelta a la posición en el ViewPager, al golpear la espalda o la navegación a partir de dicho Activity (se presupone que ha de navegación hacia arriba declarado para su Activities).

Primero debe pasar la posición actual del ViewPager como Extra en el Intent para iniciar el nuevo Activity.

A continuación, se pasa a la espalda que la posición de los padres Activity hacer 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); 

poner ese código dentro

onOptionsItemSelected (punto Menultem)

+0

Su respuesta no estaba clara, ¿puede dar algunas Ideas o enlaces relacionados? Gracias por su contribución. –

0

Creo que' he tenido un problema muy similar.

Parece que FragmentManager s se comportan 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 ello, he utilizado:

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 me queda.

más completa muestra de código es el siguiente:

FragmentManager fragmentManager = MyCurrentFragment.this.getActivity().getSupportFragmentManager(); 
FragmentTransaction transaction = fragmentManager.beginTransaction(); 

transaction.replace(R.id.container, newFragment); 
transaction.addToBackStack(null); 

transaction.commit(); 
Cuestiones relacionadas