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;
Author: AlikElzin-kilaka, 2013-04-24

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);
        }
    }
}
 15
Author: AlikElzin-kilaka,
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();
        }
    }
}
 9
Author: Walt,
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.

 3
Author: VAdaihiep,
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.

 1
Author: manelizzard,
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.

Wtedy było do dupy, ale cieszę się, że to zrobiłam. Zmusiło mnie to do myślenia o 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.
  1. 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.

  2. 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.

 1
Author: atlithorn,
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 .

 1
Author: Vikram Ezhil,
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);
}
 0
Author: Folyd,
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