2013-03-16 18 views
12

Próbuję zrozumieć, jak zachować stan widoku fragmentu, gdy fragmenty są używane w kartach nawigacyjnych. W moich wysiłkach natrafiłem na dwa problemy, na które nie mogę znaleźć właściwych rozwiązań.Karty nawigacyjne systemu Android: Przywracanie stanu widoku fragmentów

Mam dwie karty Tab1 i Tab2. Układ Tab1 jest zdefiniowany przez FragmentA, a układ Tab2 jest zdefiniowany przez FragmentB. Mam następnie podejście podane tutaj: Adding navigation tabs

Pierwszy problem: Choć moje poglądy mają identyfikatory, ich kraje nie są w pełni przywrócona, gdy fragment jest ponownie załączony (po obróceniu przełącznika Tab). W szczególności: EditText z identyfikatorem rzeczywiście zapisuje wprowadzony tekst, ale NIE zapisuje on swojego statusu włączonego. Również moje przyciski nie zapisują, jeśli są włączone lub wyłączone, mimo że mają identyfikatory. Znalazłem dwa możliwe rozwiązania dla tego problemu:

  1. Wpisz hide()/show() zamiast attach()/detach() podczas przełączania kart.
  2. w onPause() zapisz bieżący stan widoku fragmentu w zmiennej instancji View fragmentu poprzez getView(). W polu onCreateView(Bundle savedInstanceState) sprawdź, czy to pole ma wartość inną niż null, a jeśli tak jest, zwróć wartość tego pola. To rozwiązanie wydaje się być hacky i powiedziano mi, że może to również spowodować wyciek pamięci w mojej aplikacji.

Drugi problem: Rozważmy następujący interakcji użytkownika: użytkownika zaczyna się na Wypustu1 i robi kilka zmian, które wprowadzone stan widoku Wypustu1 w innym stanie niż jego stanu domyślnego (i chcemy fragment zapisać ten stan widoku za pomocą przełączników tabulatorów i urządzeń przechylonych). Użytkownik przechodzi do Tab2. Użytkownik następnie przechyla swoje urządzenie (nadal w Tab2). Użytkownik przełącza się następnie na Tab1 (przy nowej orientacji ekranu). Problem polega na tym, że: Kiedy użytkownik początkowo zamienia się z Tab1 na Tab2, fragment jest odłączany, a jego widok jest odrzucany (nawet jeśli instancja fragmentu nadal istnieje). Kiedy użytkownik przechyla urządzenie, aktywność - a przez to zarówno FragmentA jak i FragmentB z nim związane - zostają zniszczone. Ponieważ FragmentA w tym miejscu nie ma już widoku (pamiętaj: został odłączony) nie możemy zapisać stanu jego elementów widoku (np. Które przyciski są włączone/wyłączone) podczas połączenia z FragmentA.onSaveInstanceState(Bundle savedInstanceState). Jak odzyskasz stan widoku fragmentu w takiej sytuacji? Czy jedynym możliwym rozwiązaniem jest zapisanie wszystkich flag statusu poszczególnych elementów widoku jako SharedPreferences? Wydaje się to zbyt skomplikowane dla takiej "codziennej pracy".

Byłem na całym SO i różnych blogach, ale nie byłem w stanie znaleźć rozwiązania tego (konkretnego) problemu.

Odpowiedz

7

Problem 1:

Android nie oszczędza Twój widok stanu włączony domyślnie. Wygląda na to, że zapisywane są tylko rzeczy, na które bezpośrednio wpływają działania użytkownika (bez dodatkowego kodu). Dla normalnego widoku, no information is saved i dla TextView, którego EditText jest podklasą, ustawiany jest entered text is saved (jeśli jest ustawiony).

Jeśli chcesz coś jeszcze zapisać, musisz to zrobić sam. Here to pytanie z kilkoma odpowiedziami, które pokazują, jak zaimplementować zapisywanie stanu widoku niestandardowego. Możesz trzymać się za pomocą attach/dépach, jeśli podążasz za tym podejściem.

Problem 2:

Masz rację w tym Fragment.onSaveInstanceState (Bundle) może być wywoływana po twój widok już zostało zniszczone. Jednak nie jest to miejsce, w którym powinieneś zapisywać swój stan widoku. Android wywoła View.onSaveInstanceState() tuż przed zniszczeniem twoich widoków po odłączeniu fragmentu. Zapisuje ten stan i oddaje go, gdy ponownie przyłączysz ten fragment. Dokładnie tak się dzieje, gdy zwykle przełączasz się między kartami bez obracania. Fragment.onSaveInstanceState (Bundle) nie jest wywoływany podczas odłączania. Nawet jeśli obrócisz urządzenie, stan widoku zapisany w wyniku odłączenia będzie się utrzymywał. Jeśli zastosujesz View.onSaveInstanceState(), jak wskazano powyżej, twój stan widoku zostanie zapisany i przywrócony poprawnie, nawet w scenariuszu Tab1-Tab2-rotate-Tab1.

Notatka: example code w docs wydaje się mieć pewne problemy przy próbie obrócenia. Żywotność TabListener jest taka sama jak w Activity - nowa jest tworzona przy każdym obrocie. Oznacza to, że za każdym razem, gdy obracasz, traci również wewnętrzne odniesienie do fragmentu. Dodane fragmenty są odtwarzane automatycznie i nie ma potrzeby, aby TabListener próbował utworzyć nową instancję i dodać ją po obrocie. Zamiast wewnętrznego odnośnika, powinien po prostu spróbować znaleźć fragment z odpowiednim znacznikiem w menedżerze fragmentów. Po rotacji nadal będzie istnieć.

Innym problemem jest to, że wybrana zakładka nie została zapisana, ale jest to odnotowane na dole przykładu. Możesz zapisać to w Activity.onSaveInstanceState (Bundle).

+0

Wielkie dzięki - dobre wyjaśnienie. Jednak nadal nie wszystko jest dla mnie jasne. Napotkałem odpowiedź, na którą się powołujesz, ale nie udało mi się zrozumieć jej użycia. Co to jest "CustomView"? Stan widoku mojego fragmentu składa się z wbudowanych widoków (takich jak EditTexts, przyciski itd.). Dlatego nie rozszerzyłem żadnych klas widoku - po prostu zbudowałem widok fragmentów w pliku układu XML. Jak mogę podłączyć przykładowy kod podany w odpowiedzi do mojego layoutu, który nie używa żadnych zdefiniowanych przez użytkownika widoków (zakładając, że CustomView jest dla androida czym jest formant zdefiniowany przez użytkownika w ASP .NET)? –

+1

@jvmk Aby użyć metody View.onSaveInstanceState(), należy podklasować widok i zastąpić tę metodę. Twój układ XML musi odwoływać się do tych nowych widoków zamiast do EditText/Button. – antonyt

+2

Woah, to szalone. Sądziłem, że przywrócenie stanu widoku wbudowanych widoków będzie zadaniem, które często powraca na tyle często, aby można je było obsłużyć jako standardową funkcję. Przypuśćmy, że powinienem napisać bibliotekę do wykorzystania w przyszłości. Dzięki, że mi pomogłeś. To mnie denerwuje od 2 tygodni, heh. –

0
private ViewPager viewPager; 
viewPager = (ViewPager) findViewById(R.id.pager); 
mAdapter = new TabsPagerAdapter(getSupportFragmentManager()); 
viewPager.setAdapter(mAdapter); 
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { 

     @Override 
     public void onPageSelected(int position) { 
      // on changing the page 
      // make respected tab selected 
      actionBar.setSelectedNavigationItem(position); 
     } 

     @Override 
     public void onPageScrolled(int arg0, float arg1, int arg2) { 
     } 

     @Override 
     public void onPageScrollStateChanged(int arg0) { 
     } 
    }); 
} 

@Override 
public void onTabReselected(Tab tab, FragmentTransaction ft) { 
} 

@Override 
public void onTabSelected(Tab tab, FragmentTransaction ft) { 
    // on tab selected 
    // show respected fragment view 
    viewPager.setCurrentItem(tab.getPosition()); 
} 

@Override 
public void onTabUnselected(Tab tab, FragmentTransaction ft) { 
} 
+1

Należy rozważyć umieszczenie pewnych informacji na temat swojej odpowiedzi, a nie tylko opublikowanie kodu. Staramy się dostarczać nie tylko "poprawki", ale także pomagać ludziom w nauce. Powinieneś wyjaśnić, co było nie tak w oryginalnym kodzie, co zrobiłeś inaczej i dlaczego twoja zmiana (y) zadziałała. –

Powiązane problemy