5

Ho un app con la gerarchia in questo modo:FragmentTab Android host e frammenti all'interno Frammenti

FragmentTabHost (Main Activity) 
    - Fragment (tab 1 content - splitter view) 
    - Fragment (lhs, list) 
    - Framment (rhs, content view) 
    - Fragment (tab 2 content) 
    - Fragment (tab 2 content) 

Tutte le viste di frammenti vengono gonfiati dalle risorse.

Quando l'app inizia, tutto appare e sembra a posto. Quando passo dalla prima scheda a un'altra scheda e viceversa ottengo eccezioni di inflazione cercando di ricreare le visualizzazioni della scheda 1.

Scavando un po 'più profonda, questo è ciò che accade:

  • Sul primo carico, gonfiando la vista splitter provoca sue due frammenti secondari da aggiungere al gestore frammento.
  • Se si passa dalla prima scheda, la vista viene distrutta ma i frammenti figlio vengono lasciati nel gestore frammenti
  • Tornando alla prima scheda, la vista viene rigonfiata e poiché i vecchi frammenti figlio sono ancora nel gestore dei frammenti viene generata un'eccezione (per inflazione)

Ho lavorato su questo rimuovendo i frammenti figlio dal gestore dei frammenti (sto usando Mono) e ora I può cambiare tab senza eccezioni.

public override void OnDestroyView() 
{ 
    var ft = FragmentManager.BeginTransaction(); 
    ft.Remove(FragmentManager.FindFragmentById(Resource.Id.ListFragment)); 
    ft.Remove(FragmentManager.FindFragmentById(Resource.Id.ContentFragment)); 
    ft.Commit(); 

    base.OnDestroyView(); 
} 

Così ho alcune domande:

  1. sia quello sopra il modo corretto di fare questo?
  2. In caso contrario, come dovrei farlo?
  3. In entrambi i casi, in che modo il salvataggio dello stato dell'istanza lega tutto questo in modo da non perdere lo stato di visualizzazione quando si cambiano le schede?

risposta

2

Non sono sicuro di come fare questo in Mono, ma per aggiungere frammenti bambino ad un altro frammento, non è possibile utilizzare la FragmentManager del Activity. Al contrario, è necessario utilizzare il ChildFragmentManager del ospitare Fragment:

http://developer.android.com/reference/android/app/Fragment.html#getChildFragmentManager() http://developer.android.com/reference/android/support/v4/app/Fragment.html#getChildFragmentManager()

Il principale FragmentManager del Activity gestisce le schede.
Il ChildFragmentManager di tab1 gestisce le viste divise.

+0

OK, sembra promettente. Come faccio a dire al gonfiatore di usare il gestore del frammento figlio anziché quello genitore? –

+0

Aggiungete il frammento figlio all'interno del vostro frammento genitore facendo in modo che il codice del frammento genitore usi ChildFragmentManager per aggiungere i suoi figli. Quindi l'inflazione che il tuo metodo di callback onCreateView del tuo frammento (figlio) fornisce: https://developer.android.com/reference/android/app/Fragment.html#onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle). –

+0

Probabilmente hai bisogno di fare un po 'di googling/stackoverlowing per vedere come gestire i frammenti child in Mono. Non posso aiutarti (io uso Java). –

2

OK, ho finalmente capito questo:

Come suggerito in precedenza, prima ho cambiato la creazione frammento da fare programatically e li aveva aggiunto al gestore frammento bambino, in questo modo:

public override View OnCreateView(LayoutInflater inflater, ViewGroup viewGroup, Bundle savedInstance) 
{ 
    var view = inflater.Inflate(Resource.Layout.MyView, viewGroup, false); 

    // Add fragments to the child fragment manager 
    // DONT DO THIS, SEE BELOW 
    var tx = ChildFragmentManager.BeginTransaction(); 
    tx.Add(Resource.Id.lhs_fragment_frame, new LhsFragment()); 
    tx.Add(Resource.Id.rhs_fragment_frame, new RhsFragment()); 
    tx.Commit(); 

    return view; 
} 

Come previsto, ogni volta che cambio scheda, viene creata un'istanza aggiuntiva di Lhs/RhsFragment, ma ho notato che anche il vecchio Lhs/RhsFragment OnCreateView viene richiamato. Quindi, dopo ogni interruttore di tabulazione, ci sarà un'altra chiamata a OnCreateView. Cambia scheda 10 volte = 11 chiama OnCreateView. Questo è ovviamente sbagliato.

Guardando il codice sorgente per FragmentTabHost, posso vedere che semplicemente scollega e ricollega il frammento del contenuto della scheda quando si passa da una scheda all'altra. Sembra che il ChildFragmentManager di Fragment del genitore stia mantenendo i frammenti del bambino intorno e ricreando automaticamente le loro viste quando il frammento genitore viene riattaccato.

Così, ho spostato la creazione di frammenti di OnCreate, e solo se non ci sta caricando da stato salvato:

public override void OnCreate(Bundle savedInstanceState) 
{ 
    base.OnCreate(savedInstanceState); 

    if (savedInstanceState == null) 
    { 
     var tx = ChildFragmentManager.BeginTransaction(); 
     tx.Add(Resource.Id.lhs_fragment_frame, new LhsFragment()); 
     tx.Add(Resource.Id.rhs_fragment_frame, new RhsFragment()); 
     tx.Commit(); 
    } 
} 


public override View OnCreateView(LayoutInflater inflater, ViewGroup viewGroup, Bundle savedInstance) 
{ 
    // Don't instatiate child fragments here 

    return inflater.Inflate(Resource.Layout.MyView, viewGroup, false); 
} 

Questo fissata la creazione delle visualizzazioni aggiuntive e scheda di commutazione è fondamentalmente lavorato oggi.

La domanda successiva era il salvataggio e il ripristino dello stato di visualizzazione. Nei frammenti figli ho bisogno di salvare e ripristinare l'elemento attualmente selezionato. Inizialmente ho avuto qualcosa di simile (questo è OnCreateView del frammento bambino)

public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance) 
{ 
    var view = inflater.Inflate(Resource.Layout.CentresList, container, false); 

    // ... other code ommitted ... 

    // DONT DO THIS, SEE BELOW 
    if (savedInstance != null) 
    { 
     // Restore selection 
     _selection = savedInstance.GetString(KEY_SELECTION); 
    } 
    else 
    { 
     // Select first item 
     _selection =_items[0]; 
    } 

    return view; 
} 

Il problema di questo è che la scheda host non chiama OnSaveInstanceState quando si passa schede. Piuttosto il frammento del bambino è tenuto in vita e la variabile _selezione può essere lasciata sola.

così ho spostato il codice per il gestore di selezione OnCreate:

public override void OnCreate(Bundle savedInstance) 
{ 
    base.OnCreate(savedInstance); 

    if (savedInstance != null) 
    { 
     // Restore Selection 
     _selection = savedInstance.GetString(BK_SELECTION); 
    } 
    else 
    { 
     // Select first item 
     _selection = _items[0]; 
    } 
} 

public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance) 
{ 
    // Don't restore/init _selection here 

    return inflater.Inflate(Resource.Layout.CentresList, container, false); 
} 

Ora tutto sembra funzionare perfettamente, sia quando si passa linguette e cambiando l'orientamento.