29

Ho un'attività che inizialmente ospita un ViewPager, collegato a un FragmentPagerAdapter.Sostituire ViewPager con frammento - Quindi spostarsi indietro

Quando l'utente fa clic su un elemento all'interno del frammento figlio di ViewPager, sto utilizzando FragmentTransaction per sostituire una vista contenitore vuota con un nuovo frammento a cui voglio navigare.

Se si utilizza addToBackStack() sulla transazione, eseguire il commit della transazione e quindi tornare indietro, non si ritorna alle visualizzazioni di ViewPager (il layout iniziale).

Se non si utilizza addToBackStack() sulla transazione, eseguire il commit della transazione e quindi tornare indietro, l'applicazione si chiude.

Appare evidente che ViewPager non viene aggiunto al backstack (il che non è sorprendente dato che non è un frammento in sé). Ma mi aspetto che il comportamento predefinito sia che la stampa posteriore mi riporta indietro a quella attività iniziale Visualizza (il ViewPager).

Sulla base di ciò che ho letto, sembra che forse a causa di una transazione frammentata, ViewPager o PagerAdapter perdano la traccia di quale frammento dovrebbe essere visualizzato.

Sono davvero confuso con questo, ma ho finito per creare un enorme pasticcio di codice che sovrascrive il onBackPress e mostra e nasconde le visualizzazioni viewpager. Avrei pensato che esiste un modo più semplice per utilizzare i comportamenti predefiniti per eseguire la navigazione appropriata.

tl; dr

A è un Viewpager frammenti hosting. B è un nuovo frammento.

Quando si sostituisce A con B, e quindi si preme indietro, mi aspetto di tornare ad A, ma ciò non sta accadendo.

Qualsiasi consiglio sarebbe molto apprezzato.

Codice:

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 principale (contenente 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> 

risposta

3

L'unico modo per ottenerlo è:

Quando si naviga lontano da viewPager, inviare viewPager fuori dalla vista utilizzando Visiblity.GONE. Aggiungi eventuali transazioni di frammenti nel backstack.

Quando si ritorna allo schermo di viewPager (tramite una pressione indietro), si sovrascrive il pulsante premuto. Puoi controllare per vedere quanti frammenti ci sono nel backstack. Se viewPager era la prima vista prima delle transazioni frammento, è possibile verificare se il numero di voci backstack del frammento è 0.

fragmentManager.getBackStackEntryCount() == 0, non ci sono frammenti nel backstack.

Se questa affermazione è vera, è sufficiente riportare viewPager in Visibility.VISIBLE.

+1

SI! Grazie. –

33

Ho avuto questo stesso problema per molto tempo. La soluzione risulta essere molto semplice e non è necessario alcun hack con la visibilità di ViewPager. Ho descritto in questo altro problema correlato SO: Fragment in ViewPager not restored after popBackStack

Tuttavia, per rendere più semplice, tutto ciò che serve è quello di utilizzare getChildFragmentManager() nella scheda ViewPager, invece di getSupportFragmentManager(). Così, invece di questo:

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

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

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

A tale scopo:

/** 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() va bene se ViewPager è ospitato all'interno di un frammento, ma non se il ViewPager è ospitato all'interno di FragmentActivity. –

+0

Prego :) –

9

UPDATE:
Non è questo il "modo in cui Android" e si traduce in un'esperienza utente male per il caso di un listview . Invece, crea una nuova attività.

Per le persone alla ricerca di una soluzione semplice a questo problema, riassumo quello che ho fatto.

mia architettura:

  • ViewPager a FragmentActivity (ActionBarActivity in realtà, per il supporto ActionBar Ma ActionBarActivity implementa FragmentActivity.).

  • 2 schede:

    • FragmentContainer1 che si estende Frammento.
    • FragmentContainer2 che estende Fragment.

Per ogni FragmentContainer, chiamiamo getChildFragmentManager, nel metodo onCreate per esempio, e aggiungere il frammento che vogliamo mostrare in questo contenitore:

FragmentToShow fragment = new FragmentToShow(); 

getChildFragmentManager() 
     .beginTransaction() 
     .add(R.id.container, fragment) 
     .commit(); 

Non vogliamo la nostra prima frammento da aggiungere al backstack del contenitore dei frammenti perché non vogliamo mostrare il contenitore dei frammenti se premiamo il pulsante Indietro.

Poi, se vogliamo sostituire FragmentToShow da un altro frammento della nostra classe FragmentToShow (come con un controllo ListView):

Fragment itemFragment = new ItemFragment(); 

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

Qui richiamare il dirigente frammento bambino, e si aggiungono gli itemFragment allo stack indietro .

Così ora vogliamo, premendo il pulsante Indietro, per tornare a listView (l'istanza di FragmentToShow). La nostra attività (FragmentActivity) è l'unico a conoscenza del pulsante indietro, quindi dobbiamo sovrascrivere il metodo onBackPressed() in questa attività:

@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(); 
    } 
} 

Dal momento che noi chiamiamo getSupportFragmentManager nella nostra attività, abbiamo appena può chiamare getFragmentManager in i nostri frammenti infantili. Ciò restituirà un'istanza FragmentManager di supporto.

E questo è tutto! Non sono un esperto, quindi se hai suggerimenti o commenti, sentiti libero.

+0

Grazie mille suBackPressed() funziona bene. – Androidbirt

+0

Questa architettura sta funzionando per me. Grazie :) – Dory

0

Se si utilizza un ViewPager contenente Fragments che può iniziare Activities, ecco come si potrebbe correttamente tornare indietro alla posizione nella ViewPager, su colpire di nuovo o la navigazione su da detto Activity (presuppone che abbiate la navigazione verso l'alto dichiarata per la vostra Activities).

Prima di tutto è necessario passare la posizione corrente di ViewPager come extra nel Intent per avviare il nuovo Activity.

Poi si passa di nuovo quella posizione al genitore Activity fare questo:

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); 

Metti che il codice all'interno

onOptionsItemSelected (voce MenuItem)

+0

La tua risposta non è stata chiara, puoi darti qualche idea o link correlati, grazie per il contributo. –

0

Credo' ho avuto un problema molto simile

Sembra che gli FragmentManager s si comportino in modo un po 'gerarchico. Il modo in cui ho risolto questo problema è stato l'utilizzo del "principale" FragmentManager dall'attività, che ospita il contenitore e il ViewPager, e non l'unico, che può essere recuperato dai frammenti all'interno di ViewPager.

Per fare questo, ho usato:

this.getActivity().getSupportFragmentManager() 

dove this è un'istanza di frammento.

Ora se navigo da un oggetto nel ViewPager ad un altro frammento con la transazione "replace", al ritorno posso vedere il ViewPager nello stato che ho lasciato.

esempio di codice Più completo è simile al seguente:

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

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

transaction.commit(); 
Problemi correlati