Nakładające się ukryte fragmenty po zabiciu i przywróceniu aplikacji
Przełączam się między fragmentami przez ukrywając ostatni fragment i dodając nowy (Patrz kod poniżej) - dodając go również do back-stack . W ten sposób użytkownicy mogą szybko przełączać się między fragmentami bez przeładowywania danych fragmentów.
Działa to dobrze, dopóki aplikacja nie zostanie zabita (Scenariusz: użytkownicy używają kilku innych aplikacji, a moja aplikacja zostaje uparta i zabita).
Gdy użytkownik otworzy aplikację, jest ona przywracana i wszystkie pokazane są fragmenty - nakładające się na siebie .
Pytanie: w jaki sposób odtworzone fragmenty mogą zostać przywrócone z ich ukrytym stanem?Może brakuje mi flagi? gdzieś? Może istnieje lepsze rozwiązanie do szybkiego przełączania między fragmentami (bez przeładowywania danych)?
Przykładowy kod dodawania fragmentów-wywołany kilka razy różnymi fragmentami po kliknięciu gdzieś:
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.hide(lastFragment);
fragmentTransaction.add(newFragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
lastFragment = newFragment;
7 answers
Mam nadzieję, że ktoś znajdzie lepsze rozwiązanie. Poczekam na takie zanim zaakceptuję moje rozwiązanie:
Ogólnie rzecz biorąc, używam wygenerowanych tagów, aby znaleźć nieujawnione fragmenty i je ukryć.
W szczegółach, generuję unikalny znacznik dla każdego fragmentu (StackEntry) i stosuję znaczniki, gdy same fragmenty zostaną ułożone w stos. Utrzymuję stos w bundel i ładuję go, gdy aplikacja zostanie przywrócona, aby kontynuować korzystanie z niego. Następnie używam listy tagów, aby znaleźć wszystkie unhidden fragmenty i ukryć je-z wyjątkiem ostatniego.
Heres przykładowy kod:
public class FragmentActivity extends Activity {
private static final String FRAGMENT_STACK_KEY = "FRAGMENT_STACK_KEY";
private Stack<StackEntry> fragmentsStack = new Stack<StackEntry>();
public FragmentActivity() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.content_frame);
if (savedInstanceState == null) {
// Init for the first time - not restore
// ...
} else {
Serializable serializable = savedInstanceState.getSerializable(FRAGMENT_STACK_KEY);
if (serializable != null) {
// Workaround Android bug.
// See: http://stackoverflow.com/questions/13982192/when-using-an-android-bundle-why-does-a-serialised-stack-deserialise-as-an-arra
// And: https://code.google.com/p/android/issues/detail?id=3847
@SuppressWarnings("unchecked")
List<StackEntry> arrayList = (List<StackEntry>) serializable;
fragmentsStack = new Stack<StackEntry>();
fragmentsStack.addAll(arrayList);
}
// Hide all the restored fragments instead of the last one
if (fragmentsStack.size() > 1) {
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
for (int i = 0; i < fragmentsStack.size()-1; i++) {
String fragTag = fragmentsStack.get(i).getFragTag();
Fragment fragment = getFragmentManager().findFragmentByTag(fragTag);
fragmentTransaction.hide(fragment);
}
fragmentTransaction.commit();
}
}
getFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener() {
@Override
public void onBackStackChanged() {
Fragment lastFragment = getLastFragment();
if (lastFragment.isHidden()) {
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.show(lastFragment);
fragmentTransaction.commit();
}
}
});
}
private Fragment getLastFragment() {
if (fragmentsStack.isEmpty()) return null;
String fragTag = fragmentsStack.peek().getFragTag();
Fragment fragment = getFragmentManager().findFragmentByTag(fragTag);
return fragment;
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable(FRAGMENT_STACK_KEY, fragmentsStack);
}
@Override
public void onBackPressed() {
if (!fragmentsStack.isEmpty()) {
fragmentsStack.pop();
}
}
public void switchContent(Fragment fragment) {
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
Fragment lastFragment = getLastFragment();
if (lastFragment != null) {
fragmentTransaction.hide(lastFragment);
}
String fragTag;
if (fragment.isAdded()) {
fragmentTransaction.show(fragment);
fragTag = fragment.getTag();
} else {
fragTag = Long.toString(System.currentTimeMillis());
fragmentTransaction.add(R.id.content_frame, fragment, fragTag);
}
if (!isFirstFragment()) {
// Add to backstack only the first content fragment and not the state before (that has nothing)
fragmentTransaction.addToBackStack(null);
}
fragmentTransaction.commit();
fragmentsStack.push(new StackEntry(fragTag));
}
public boolean isFirstFragment() {
return fragmentsStack.size() == 0;
}
private static class StackEntry implements Serializable {
private static final long serialVersionUID = -6162805540320628024L;
private String fragTag = null;
public StackEntry(String fragTag) {
super();
this.fragTag = fragTag;
}
public String getFragTag() {
return fragTag;
}
}
public static class Intent extends android.content.Intent {
public Intent(Context packageContext) {
super(packageContext, FragmentActivity.class);
}
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-25 17:14:48
Miałem również ten problem, Oto jedno możliwe rozwiązanie: niech każdy fragment zapisuje swój stan, czy był ukryty, czy nie, a następnie ukryje się w swoim onkreacie.
@Override
public void onSaveInstanceState(Bundle bundle) {
super.onSaveInstanceState(bundle);
if (this.isHidden()) {
bundle.putBoolean("hidden", true);
}
}
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
if (bundle != null) {
if (bundle.getBoolean("hidden", false)) {
getFragmentManager()
.beginTransaction()
.hide(this)
.commit();
}
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-15 04:07:37
Wreszcie znalazłem najprostszy sposób, aby rozwiązać ten problem: Zmień content_frame z Framlayout na LinearLayout.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-07 02:17:30
Miałem ten sam problem i rozwiązałem go ustawiając setRetainInstance(true);
w metodzie onCreate()
każdego fragmentu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-18 11:54:11
Miałem dokładnie ten sam problem. Niestety jedynym dobrym rozwiązaniem było przejście na użycie fragmentTransaction.replace
zamiast fragmentTransaction.hide
i dodanie.
savedInstanceState
i właściwego radzenia sobie z tym. Wspomniałeś, że podczas powrotu fragment jest przeładowany. Miałem dokładnie ten sam problem, który zmusił mnie do odpowiedniego postępowania. Są tam 2 przypadki.
Jeśli działalność nie została zniszczona jedyną rzeczą, która potrzebne odtworzenie był widok (via
onCreateView
) Wszystko inne jest nadal w pamięci, więc było to tylko kwestia podłączenia adaptera do widoku i gotowe.Jeśli aktywność została zniszczona, musiałem odtworzyć widok i adapter. W celu zminimalizowania czasu ładowania zapisałem dane potrzebne do odtworzenia adaptera w
savedInstanceState
Twój gripe jest jednak ważny, Nie wiem dlaczego Android nie obsługuje powrotu z poprawnym ukrytym stanem ze zniszczonej aktywności z fragmentami, które używały add and hide.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-26 10:44:56
Skoro wspomniałeś, że nie masz nic przeciwko, aby fragmenty ładowały się od nowa, dlaczego nie użyć intent i nie zacząć od nowa głównej aktywności fragmentu?
Stanąłem przed tym samym problemem, jak ten, o którym wspomniałeś, gdzie fragmenty nakładały się na siebie. Przejrzałem cały stackoverflow i znalazłem tylko ten wątek, w którym omawiano ten konkretny problem. Próbowałem rozwiązania dostarczonego przez Walta , ale nie zadziałało zgodnie z oczekiwaniami.
Poniżej obejście działa przynajmniej dla mnie, więc dzielenie się nim na wypadek, gdyby ktoś skończył z tym scenariuszem
At onSaveInstanceState w macierzystym fragmencie ustawiłem znacznik, aby upewnić się, że coś jest zapisane w pakiecie.
public void onSaveInstanceState(Bundle outState)
{
// TODO Auto-generated method stub
super.onSaveInstanceState(outState);
outState.putString(TAG, "Get ready to be terminated");
};
I wewnątrz onCreate możesz określić klasę do załadowania używając Intent, gdy zapisany stan instancji nie jest null,
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
this.setContentView(R.layout.layout);
if(savedInstanceState != null)
{
Intent myIntent = new Intent(this, your.class);
// Closing the parent fragment
finish();
this.startActivity(myIntent);
}
else
{
// your main code
...........
...........
};
};
To sprawi, że Twoje fragmenty zostaną odtworzone od nowa.
W moim przypadku, gdy użytkownik otworzył moją aplikację miałem ekran logowania i jeśli byli już "zalogowani", zanim zostaną przekierowani na ekrany fragmentów.
Podczas procesu zabijania, ponownie kieruję użytkowników do strony logowania, gdy aplikacja pojawi się na pierwszym planie i od tego momentu mój już istniejący kod zajmuje się ponownym kierowaniem użytkownika z powrotem do nowo utworzonych ekranów fragmentów.
Uwaga: musisz upewnić się, że fragment Twojego dziecka nie ma luźnych końców w onCreateView .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-07 19:38:38
Spotkałem się z tym samym problemem,myślę, że to jest Android framework bug.Oto numer .
Jednak mój sposób będzie działał dla Ciebie, powinniśmy nadpisać metodę onSaveInstanceState(Bundle outState)
, zapisać nasze niestandardowe dane do outState
, ale nigdy nie wywołać super.onSaveInstanceState(outState);
.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
......
if (savedInstanceState != null) {
mCustomVariable = savedInstanceState.getInt("variable", 0);
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
//super.onSaveInstanceState(outState);
outState.putInt("variable", mCustomVariable);
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-12 07:59:06