5

Mam app z hierarchią takiego:gospodarz Android FragmentTab i Fragmenty wewnątrz Fragmenty

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

Wszystkie fragment poglądy są zawyżone z zasobów.

Po uruchomieniu aplikacji wszystko pojawia się i wygląda dobrze. Kiedy przełączam się z pierwszej karty na inną kartę iz powrotem, dostaję nadęty wyjątek, próbując odtworzyć widoki z karty 1.

Kopiąc głębiej, to jest to, co się dzieje:

  • Na pierwszym obciążeniu pompowania widok rozgałęźnik powoduje jej dwa fragmenty dziecko mają być dodane do kierownika fragmentów.
  • Po przełączeniu z pierwszej karty widok jest zniszczony, ale fragmenty podrzędne pozostają w menedżerze fragmentów.
  • Po przełączeniu z powrotem do pierwszej karty widok jest ponownie zawyżany, a stare fragmenty podrzędne są nadal wyświetlane. w menadżerze fragmentów generowany jest wyjątek, gdy tworzone są nowe fragmenty podrzędne (przez inflację):

Pracowałem nad tym poprzez usunięcie fragmentów potomnych z menedżera fragmentów (używam Mono), a teraz może przełączać karty bez wyjątku.

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

więc mam kilka pytań:

  1. Czy powyżej poprawny sposób to zrobić?
  2. Jeśli nie, jak mam to robić?
  3. Tak czy inaczej, w jaki sposób zapisywanie stanu instancji wiąże się z tym wszystkim, aby nie tracić stanu widoku podczas przełączania kart?

Odpowiedz

2

Nie jestem pewien, jak to zrobić w Mono, ale dodać fragmenty dziecko do innego fragmentu, nie można używać FragmentManager z Activity. Zamiast tego trzeba użyć ChildFragmentManager z hostingu Fragment:

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

Głównym FragmentManager z Activity obsługuje swoich kart.
Obiekt ChildFragmentManager z tab1 obsługuje widoki podzielone.

+0

OK, to brzmi obiecująco. Jak mogę powiedzieć, że nadmuchiwacz używa menedżera fragmentów potomnych zamiast macierzystego? –

+0

Dodajesz fragment podrzędny do wnętrza fragmentu macierzystego, używając kodu fragmentu nadrzędnego, używając programu ChildFragmentManager do dodania jego elementów podrzędnych. Następnie nadmuchiwania, które metoda callback onCreateView z części (dziecko) zapewnia: https://developer.android.com/reference/android/app/Fragment.html#onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle). –

+0

Prawdopodobnie będziesz musiał wykonać więcej badań google/stackoverlowing, aby zobaczyć, jak radzić sobie z fragmentami potomnymi w Mono. Nie mogę ci tam pomóc (używam Java). –

2

OK, w końcu zorientowaliśmy się:

Jak sugerowano powyżej, pierwszy zmieniłem tworzenie fragmentów do zrobienia programowo i miał je dodany do kierownika fragmentu dziecko, tak jak poniżej:

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

Zgodnie z oczekiwaniami, za każdym razem, gdy przełączałem karty, tworzone były dodatkowe instancje Lhs/RhsFragment, ale zauważyłem, że stary OnLeon/RhsFragment również zostanie wywołany. Tak więc po każdym przełączniku tabulacji byłoby jeszcze jedno wywołanie OnCreateView. Przełącz zakładki 10 razy = 11 połączeń do OnCreateView. Jest to oczywiście błędne.

Patrząc na kod źródłowy FragmentTabHost, widzę, że po prostu odłącza i ponownie dołącza fragment zawartości karty podczas przełączania kart. Wygląda na to, że nadrzędny Fragmentowy obiekt ChildFragmentManager zachowuje fragmenty potomne i automatycznie odtwarza ich widoki, gdy fragment macierzysty jest ponownie dołączany.

Więc przeniosłem tworzenie fragmentów do onCreate, i tylko wtedy, gdy nie jesteśmy ładowania z zapisanego stanu:

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

To stałe tworzenie dodatkowych widoków i zakładka przełączania w zasadzie pracował teraz.

Następne pytanie dotyczyło zapisywania i przywracania stanu widoku. W fragmentach potomnych muszę zapisać i przywrócić aktualnie wybrany element. Pierwotnie miałem coś takiego (to fragment podrzędnego OnCreateView)

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

Problem polega na tym, że host zakładka nie wymaga OnSaveInstanceState podczas przełączania kart. Zamiast tego fragment dziecka zostaje utrzymany przy życiu, a zmienna selekcji może zostać pozostawiona sama.

Więc przeniosłem kod zarządzać wybór do 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); 
} 

Teraz to wszystko wydaje się być doskonale pracuje zarówno podczas przełączania kart i zmiany orientacji.