web-dev-qa-db-de.com

Warum behält Fragment den Status nicht bei, wenn der Bildschirm gedreht wird?

Es ist mir nicht gelungen, einige benutzerdefinierte DialogPreference-Unterklassen in einem PreferenceFragment sichtbar zu machen, wenn der Bildschirm gedreht wird. Wenn ich PreferenceActivity verwende, tritt dieses Problem nicht auf. Daher weiß ich nicht, ob es sich um einen Android-Fehler oder ein Problem mit meinem Code handelt. Ich möchte jedoch, dass jemand bestätigt, ob er dieselbe Erfahrung hat.

Um dies zu testen, erstellen Sie zunächst einen Einstellungsbildschirm mit mindestens einer DialogPreference (unabhängig von der Unterklasse). Zeigen Sie es dann in einer PreferenceActivity an. Wenn Sie Ihre App ausführen, drücken Sie auf die DialogPreference, damit das Dialogfeld angezeigt wird. Drehen Sie dann den Bildschirm, damit sich die Ausrichtung ändert. Bleibt der Dialog sichtbar?

Versuchen Sie dann dasselbe, aber mit einem PreferenceFragment, um Ihre Einstellungen anstelle einer PreferenceActivity anzuzeigen. Bleibt der Dialog wieder sichtbar, wenn Sie den Bildschirm drehen?

Bisher habe ich festgestellt, dass das Dialogfeld sichtbar bleibt, wenn eine PreferenceActivity verwendet wird, nicht jedoch, wenn ein PreferenceFragment verwendet wird. Mit Blick auf die Quellcode für DialogPreference , so scheint es, dass das richtige Verhalten ist für den Dialog sichtbar zu bleiben, weil isDialogShowing der Informationszustand ist, die gespeichert wird, wenn onSaveInstanceState() auf dem Bildschirm Umorientierung aufgerufen wird. Daher denke ich, dass ein Fehler möglicherweise verhindert, dass das PreferenceFragment (und alles in ihm) diese Statusinformationen wiederherstellt.

Wenn es sich um einen Android-Fehler handelt, hat dies weitreichende Auswirkungen, da jeder, der PreferenceFragment verwendet, Statusinformationen nicht speichern und wiederherstellen kann.

Kann das bitte jemand bestätigen? Wenn es kein Fehler ist, was ist dann los?

27

Endlich eine Lösung für dieses Problem gefunden. Es stellt sich heraus, dass dies kein Fehler ist, sondern ein Problem/Versehen in der Android-Entwicklerdokumentation.

Sie sehen, ich folgte dem PreferenceFragment-Tutorial hier . In diesem Artikel werden Sie aufgefordert, Folgendes zu tun, um Ihr PreferenceFragment innerhalb einer Aktivität zu instanziieren:

public class SettingsActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Display the fragment as the main content.
        getFragmentManager().beginTransaction()
                .replace(Android.R.id.content, new SettingsFragment())
                .commit();
    }
} 

Das Problem dabei ist, dass das PreferenceFragment erstellt wird, wenn Sie die Bildschirmausrichtung ändern (oder jede andere Aktion, die die Aktivität zerstört und neu erstellt) zweimal . Dadurch wird ihr Status verloren. 

Die Erstellung von first erfolgt über den Aufruf der Aktivität an super.onCreate() (oben angezeigt), die die onActivityCreated()-Methode für Ihr PreferenceFragment () und die onRestoreInstanceState()-Methode für jede darin enthaltene Voreinstellung aufruft. Diese stellen den Zustand von allem erfolgreich wieder her.

Sobald der Aufruf von super.onCreate() zurückkehrt, können Sie jedoch feststellen, dass die onCreate()-Methode dann das PreferenceFragment a second time erstellt. Da es wieder sinnlos erstellt wird (und diesmal ohne Statusinformationen!), Wird der Status, der gerade erfolgreich wiederhergestellt wurde, vollständig verworfen/verloren. Dies erklärt, warum ein DialogPreference, das möglicherweise zu dem Zeitpunkt angezeigt wird, zu dem die Aktivität zerstört wird, nach dem Erstellen der Aktivität nicht mehr sichtbar ist.

Also, was ist die Lösung? Fügen Sie einfach eine kleine Überprüfung hinzu, um zu bestimmen, ob PreferenceFragment bereits erstellt wurde.

public class SettingsActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Fragment existingFragment = getFragmentManager().findFragmentById(Android.R.id.content);
        if (existingFragment == null || !existingFragment.getClass().equals(SettingsFragment.class))
        {
            // Display the fragment as the main content.
            getFragmentManager().beginTransaction()
                .replace(Android.R.id.content, new SettingsFragment())
                .commit();
        }
    }
}

Oder Sie prüfen einfach, ob onCreate() den Zustand wiederherstellen soll oder nicht.

public class SettingsActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (savedInstanceState == null)
        {
            // Display the fragment as the main content.
            getFragmentManager().beginTransaction()
                .replace(Android.R.id.content, new SettingsFragment())
                .commit();
        }
    }
}

Ich denke, die Lektion, die wir hier gelernt haben, ist, dass onCreate() eine doppelte Rolle hat - es kann eine Aktivität zum ersten Mal eingerichtet werden oder es kann aus einem früheren Zustand wiederhergestellt werden.

Die Antwort hier brachte mich dazu, diese Lösung zu realisieren.

49

Ich habe dieses Problem in der Tat selbst gehabt. Es gibt einen Fehler, bei dem die DialogFragment den Zustand nicht wiederherstellt, weil sie null ist oder zumindest mir passiert ist.

Mit mehreren Quellen bekam ich schließlich eine Lösung zum Laufen. Lassen Sie Ihren Dialog diese BaseDialogFragment erweitern:

import Android.app.Dialog;
import Android.os.Bundle;
import Android.util.Log;
import Android.view.LayoutInflater;
import Android.view.View;
import Android.view.ViewGroup;
import Android.support.v4.app.DialogFragment;

import com.actionbarsherlock.app.SherlockDialogFragment;

public class BaseDialogFragment extends DialogFragment {

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        if (savedInstanceState == null || savedInstanceState.isEmpty())
            savedInstanceState = WorkaroundSavedState.savedInstanceState;

        setRetainInstance(true);
        Log.d("TAG", "saved instance state oncreate: "
                + WorkaroundSavedState.savedInstanceState);
        super.onCreate(savedInstanceState);
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState)
    {
        if (savedInstanceState == null || savedInstanceState.isEmpty())
            savedInstanceState = WorkaroundSavedState.savedInstanceState;
        Log.d("TAG", "saved instance state oncretaedialog: "
                + WorkaroundSavedState.savedInstanceState);

        return super.onCreateDialog(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        if (savedInstanceState == null || savedInstanceState.isEmpty())
            savedInstanceState = WorkaroundSavedState.savedInstanceState;

        Log.d("TAG", "saved instance state oncretaeview: "
                + WorkaroundSavedState.savedInstanceState);

        return super.onCreateView(inflater, container, savedInstanceState);
    }

    @Override
    public void onDestroyView() // necessary for restoring the dialog
    {
        if (getDialog() != null && getRetainInstance())
            getDialog().setOnDismissListener(null);

        super.onDestroyView();
    }

    @Override
    public void onSaveInstanceState(Bundle outState)
    {
        // ...

        super.onSaveInstanceState(outState);
        WorkaroundSavedState.savedInstanceState = outState;
        Log.d("TAG", "saved instance state onsaveins: "
                + WorkaroundSavedState.savedInstanceState);

    }

    @Override
    public void onDestroy()
    {
        WorkaroundSavedState.savedInstanceState = null;
        super.onDestroy();
    }

    /**
     * Static class that stores the state of the task across orientation
     * changes. There is a bug in the compatibility library, at least as of the
     * 4th revision, that causes the save state to be null in the dialog's
     * onRestoreInstanceState.
     */
    public static final class WorkaroundSavedState {
        public static Bundle savedInstanceState;
    }
}

Beachten Sie, dass Sie in Unterklassen, deren Methoden über einen savedInstanceState-Parameter verfügen, möglicherweise Super mit WorkaroundSavedState.savedInstanceState aufrufen müssen. Wenn Sie den Zustand wiederherstellen (d. H. In onCreate(), ignorieren Sie einfach die savedInstanceState und verwenden Sie stattdessen WorkaroundSavedState.savedInstanceState.) Der statische Halter ist nicht die sauberste Lösung, aber er funktioniert. Stellen Sie sicher, dass er in Ihrer onDestroy() auf null gesetzt ist.

In jedem Fall verschwindet meine DialogFragment nicht, wenn ich den Bildschirm drehe (und das ohne configChanges). Lassen Sie mich wissen, ob dieser Code Ihr Problem anspricht, und wenn nicht, schaue ich mir an, was los ist. Beachten Sie auch, dass ich dies nicht in PreferenceFragment getestet habe, sondern stattdessen andere Fragments der Kompatibilitätsklasse oder von ActionBarSherlock.

0
Oleg Vaskevich